From b3d0a577bbd825c5414f7485dba07736d3f5584d Mon Sep 17 00:00:00 2001 From: Steven Robertson Date: Wed, 24 Nov 2010 09:39:29 +0000 Subject: [PATCH 01/63] Updating the installer images. --- admin/win/page_header.bmp | Bin 25818 -> 25818 bytes admin/win/welcome.bmp | Bin 154542 -> 154542 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/admin/win/page_header.bmp b/admin/win/page_header.bmp index 2ca78cd428c121dd7fdd1a76afb1fc56d0e7dbfc..c50025a22b810a7e755768c16e73ce7f1ec97ea1 100755 GIT binary patch delta 3985 zcmZXX30PCt635R?LJ~qqAZ$U_gngIAC?M2;iXvbXR2I=75J6GdL{RokKv6;v3yP4s z0kRmQNFNBUxKKe65m2E-+e&M#+FHBXH%UZ&efK-x&H3QF=XYl2KXc}c-^9jmVyCKp^VX)epPs&&8vbr?S^0tm3q($K1|t4i zmXx(F3QPF=@4thBf_!~_3wP!ntv?Rm?ccvYA|m`+Pv5gg-@o|!mkZ}EdU<)RSg`^& zF*P*>J5WAAgPn;)A^^t4#pUPc_x1Jt@y8!C|Cj$hfBw9vs0g;(*x2~zpMOfYp;!WZ z2YcEm5EO0OGC3%_+ds_VaLmliE_Pm=x+|L=fAMHQmYtP@$Kw$~PEJlg{`e!T^y<|s z@Q^xG-BS0R!XE?hV?G^At#oX6wASMU%BgqoTf373mud~jlXLqh}X zb4zIO(ejGtk6(}8edspFt-bN|`1OZh4?cZ4_2yXJNn0CR#kbhfrAu$!y5-~Jqor>e z6t{b;s5@QU&z={+V7r()`Ovgx>FMcRx^(H+UwU1gSFXh2a363miHL}RI7VRc;MTOXwA`%B zYyrO{adYqS?uUIcsqjp9{rT~}$?1_7kGm(fMJG}8STtjMI@^V)MvpE!@qv|f@-7$% zI5m9>nxXZarLhF!N4u0n2-4u-pxmdTdB)m8o@%sACIeqpwQT$&zzalds~Tr6fVO~{)1syZgKeS*QLoKyF=AMW$rki(i6NN3Gf5-GZ-R_;7A zC+?@F!Tj*y!xHX73~Ls04Wq$A5L0G)dSSxGWBV$;9(a1HxUFsP=|f3%1LuaG-27&+ zalqQj0Z*oaZLzx>!M4Qe-u3w>H7)09n7PtS9Y4S{jcmO`QvYRPTU(oi8-wv*ym;a1 z=~-1)k{=&^sHo`X>HZVBO?9aaEhX(^y<-o*9QXI~x3aR)GqK8Rd60ShPE1MLsPEI8 zcZRu}rPnIAfK7f;#RldsN){C8Cr_RLOImDW0=1YK302n8j2*Vb7vfZvl{s{2aQN~) z+0b|PCXSq?5%GJ^@e)KUwp0o7n+n@TLFjv=kMmD_MK^H(1lVQD336P#VL@1?z2^!H z!+?UrsX>t782I+vZ-w01Qhz%`02#>j=RU_R#CJhnT=d?Q)PvbMAi=tlFHCG)cO4%H zO{)*ydLXaiMs1g@^8BQ-Qfv}-s2kY+`w=8(YG zuU{8(9i%h|8nA4l;V{X_$Y4_R*LWmlZ#-O{bL_0RdGor|t)lZ#@*;NBm$VE6!jmT1^Y9Ne8RBI!tcVW0iIyyQdBO^kt zt%&Bplz#6>0HkqrX=y1!76_Ffv=hU^?JX<=qjv0qG%9X~*yOi9fV_oK(E4DvNDMixMS5ecXY8kjLj5Y5Nq&M_~@{L>;0TrCe zu=db#S`33><4L0H;Yk!Ljz1_L!ItXTo}9Ca^X6ri@%P8ueSYX=JP{VCW|1;XeJIc z)@*e{TcE?7VeP4D<&Gz+s%hv_)ENjP5!IT=^vkLm-5Aa+F)&t=$nL0P1+T|O;~@^EVC zf_qh{3>;AvM^I6t>mY=wkSj^FK2^;wJTZ9g+_@kcA=g~)VNtysv6Ax=loY>yJ#ZnW zfQIpmKxVlvTn!b=b#cB8Z2XZw340h-DnsM5e(3`d}- zYeAmiNqHpoelqA1)7NWXlc(}5(JkS4=oQu6*J&()EL&Y%pB7jZlu=rElt?( z7Lx235a-|@>l_sCz9z*_P`shIIjQC%)7ImoglE8DGl1nPMi`!$K~%d#qFp7BlV`vN z(kORp1ympqAe1!&{XxsX+t_-Zckssi)=>~o=7~EQ;{F{+ZloQ#vGeF{xvUijx?5n( z|3krAyb6g(R#Qk>NK_jpso%g8w^~?OD6C~l*`Bj7VW5qmva%APfEnmpUE^>#rwz=V z`FY}P2mZ6?>?0UOr^l6PswBcYXzN}I)9ha^#;JgSjy0ScOaYV(B9V>Iw35uw&vx(%vF0peIe2Wy7uR2sRed>GCY6&vFkuo? z+Dc)X|1Va3h6z|5JaL3Tp1={N5HctzD3Ea1VS+br-nhEDAtX|wRH^4&d?TDbTjMf+ zS$N9c^LM`&Uy+GsXax$N0xG%oBF)I=Qxsm|O@(>z^2e~Z81^T$6BLTt@bIvN8-?*( zT3U!iBZT?^g~LtTd2I9y5ajPJUmBUzKk>3-@R|5BpeD+s59KaZpmrX+t!e7`KO7Qe z1_Bjbtk^Xq`>dFlnD>3)96kgQI%fjG6rpB>-e?=vEDB9-ZfRQ_w5GN9?mgM-j=|}F zu|P0dX3n32aCpKD$W9T77KB*P%PWrEjaWFezCxjp$pnLf%@}%<1iBg3@kRKW*_DTX#Lsy=0@6RNhx;o_3Nj#n$8yl-wJ`oG*>FFVn zY)GW@iK*F}(o3aMX>r*;|247K??1Ua_3GT^~#U!McR2&8@JW%1`6&Cif>H4rW6BCoJuCAJzHWig?usc6LKe&p?*INQz zES!Ki92-jZiWPgHRmv$ST^*fL@%hoVb_uLnQUX=<;{*Nv;>C-{#>P&ZkW#7V5Q>8~ zXkcJK!p*^a-oAYs8XAg_J%(NO_TD!(Hh%l|z^23s3{|5ZXp0l8ziX0!}N!=D&XZ8ZK!#+$_iu2IEv_rWl$0k~iR z5V{ojOG`^9hjR#rgBAR!RP{rL4!sxpS`Hk%)z#J7+PVmFF-%OOHG6xDckOBviCSuE z+KY>u!o!c6m>kF9+7Q};VH(cP&TwkN2C6UzxLBs7q%fKK7zPK<5`;1kDp%Bje1swq zvLz76pq$#;TG*=mvdjAU=bt+}JK?gteED(@4-W?i2QHVpcJ11Vii-aJen>jS#ap^y KT`kHT-~Z37(c0_k=<2?`&pqe7d+&RLjflZUL|)ku1c`O# zcJmR9{r&yle*0}_XJ>tVeR+9#adB~BVFB8em6grS&DXDA@9pieY-CLk_-mhEgu%PJ zyGu(;!^6YP&CO+HWpp|{H8nMvNK8sjCXvbM>FEpxqoSgsqoZSde0*(f?d{vQaQO`c z!kZvwc9PH%eWMN_P;Nio&CiiaLxpV8z?b~PFhCm51mKkdULPhGSHtQR>+d>iN+$Xu*aa=-2K}R4?#D z`kG(8^V6L>cN`rZi9}*+YwPOj>brOE7{=y^UqAz5H#j&*qtVc4^o`rM&Yvp^nqBc4WarC|f4+bJzK@R&o6Uwf5H_w} zW?U0QAnNeOYEM84f&(dtjg2)kGyBZ!wkq9Ivo}$zFX?0G^d%$vNk;wT^L^w`2dU74 z4L#8R@i4G8I^#56+Wpn!Cd3(13}I)Het3YeJQQkaX$cDpyZhk2PJo3T_T?vLg$>(}VmwU)p`ct6=8+xFhH=(Zh!@(b}RV)uWhx*0b+#KQvF$1|f z2pf=t8fb5C4-E~yZhBvtgjQ;aQfiG-K7dMVv}#+ldRvTUTda0ltX_MZQAfhX&g9Qu zk*@ZpLVK|@33{L({)I6x62`+-hnEA7Vj~`=@Yw8E5{Wc2GIAJHMFbXVxWB(YIy(Bg z*+cnESGoFdfP6!^LPLaNLxgf8Uac`wqmiK96s6k~g=~sB+YC0X!v|vMzQEMEsfuLK5*RI2>*G9lqa4j$Zk8~A)N2?xYg|fFRDJg-Q7y{4phVji; z2rSre6LUBm8wWduc$5S;03gW?kSfQ?R0Jwi2B}nrXjF!3SBC1>;!iil7&b&BYw)LQ z3Fqr$K5K}((w_WTQ~ZU7Sfl!AgZik`&9SFjUh3CHYFCG;SA{B9hR9b20S~YNCy$)t zZ|Gz0@9z)g{N~M@-&v_Ye1pKkqSV*d2W6m-dnQ)m12|RUBVOt&S>`9p2~gq&s&j+1 z%7c-0QF_%8T4h1%r8s~VH(0+S%%}=~zVYRm8iGMhq)v5&W@VTr&)qO&eUw3CjCMt+ zT6wS{HxPJ$4LE@r_(cmnzjZRFP$;vrv->PtSwjTjvXTTZ8W0~QlX>0cxiHgBq|g%} z%Jw={D7Z<2hTyT(7BI*Rw4RDg>E|TvdUf?Oq_E9eK(<}?ptBO!7^iyJa$>zFAWIBnZ zJBX&)OVH4=Ss3M9cb(Eem3(i70&nR8FY$b=M1iL?+eg0GPo*S4qcl*zCQ_p;P^rjI zhV2cUzzqC{LZ^eClLg3RSU({q4CAW^EUZCLKHvM<3DKQ}axs7tc`l+1H%S&2_|=O1 zb*sWv^L-TZuu@qVQL5buqV>rHOQCpk;Up^wl8qeIL7nBTknJj+<0?jXIho^pf{qc% za}{TL$P{=f7W%5O{dKD&RM~#=1>V3Z!f*rrLm?rWBZ}V-3pK;LiSgIAAz5O)WI^`g z6A~m6Yy`5<0Ksgh6FJUexvnw{4~U3vWw=Vdk9@9&M5gmeimhOx<+11|DMNK%XZjtF zFcpn9lZv-e$-u}^>?NoU!s!lz>2?A%d%;Y{wZK=WB21CxCC%^< z&2>GV<1CQP(-JtIg+3pK%FoY-`-fqyiy(ZbDaOlKcXzj0xYyBiyQ4IFfIx=Bi7Y4a z92dD<4~-HW%<@l%Py&gT{LxSNLhp0u`^Vm{MYORMBhAGl9?KJLB;%fmC0d_IetwK- zeU$wCXsXSzG?XCCK{(4frWSjWOLn5<+uREoG{H^i1>n#@DdiCgS=@W zu+AS=o<9sIG1wE&MLF_OZ2|mLlt7xja0Xg3%SD0iu2mMSK=+W!auKH53nW|d$C~kl zKR6O_yL`TXa&H6Bwl;Z-9x5ASaVp$YIP!^5v>9(qoH<{TB|kWu@#^Te&1VsxVzvSAKsKd`d9uq z3&CV-V31CCQp|MKWCzI7oPqxY1$7jrDonHnVX=7Y;^Jc1GkI?PO<&<)^`4%Yv7+FQ zC0Yq4SqmjU7be+CP#omboYgYiblEs)<AH!8AktgeDX1pFlf)}kRF0t=_l(Xp|= zki1XETAqrtmP&Y`K(tp+b=FJwIK%Ki%kW3iu$ojCC8C3D+;izD3(3$Y;(jJ#*jr8W zBP;KA{|idTIxj}sNd!KSj4+dlex{g+(nxXA2U=!;5#3iW-CdpHEH6Q{14$4Dd6(Rz z<<*rxy`vlPynDY7vHOCA6?n)xRf>~lhPxi!`!qA)LP;0^`1SI9wIt|X zYFQp24D$AQiT@ir!hHh~g4fv{aPa-q%=A?fRv7>E6v0v|##%8Rr9njNQ(VvF_@2%7 zL8iKCrC?Mk&Po)tVv3^z5hWLAB||W4U!K_5fAe2ZzcBngJy1T$Mv-Kvl;QwBYEdvq ziW|@xWqIpST-Awa1&MfDkOX0ncVH&3ZLI$hz5@rpIX{Q>{QtEGPegx7@i`vySR~Bs zRD`*7l$BDPttQdQfbM&m9jsq~(d48HH7{!;iQUoiI1YwYOXQys$ zZ~y5o@%+)qi|g2fv)RWUudT1&$_N$0JvbitNF?l6=|o$|h8D|DpB-pe5usZUpvm%8 zp?k{HTx3%mq!V695}t~OJnCAW+KV3UMHynuC=g+KWa!1z{oFLwqHZK>9(RAZ#GR;$Aw2z`_HNT_H)({}=H@ z8yOk~&eBRb9_qPX+Du=A@^EOuR?2ddrJ|*YD9M*r;t@~80)OdVntb#A-NzW)T=J{S zH7krmbE%ZzLy7hvBP{>!qCOCYS0Y zon$K!`|MQs6VZVCy-QQSz285?_THQ1p30xHgKp-AnH42qsx$HJCA7iDis}B!xgm?@ zY$Q8ahZUgB@YP}ZAJ*}08T1oxT<7(fNzjaWJ$1Ao;TcdP<*nmHbCI;W~Txl^es)l`+Xl!IoS*D zUCs3QHY?y(L6k)~*{y+&@8Zygn>jQ69V^q5TdSoDL${jJ&T;T(Dik913UIt>XSst6 zh#jh)PQ<`w%j)}F7lGL1oi^csXOQ3|o9YBZbkMTzVnmtZ3kZRu8(p&9!^ImSvS$>Cg@`&UKb ze=j23txU3LpkdlsA$?rZSQBfur*?6)XMJvRdv$(ybK%X_#Lim4Q03){7-V4(FPB*! zP#-b^B>SSF4oK9&*%!V$=EfxBZ8UN`51~o-*2(ilvI2|>0xwh$&J~9mGX3>(ykR3# zqc|xi+bJg4D8YE5BQnkH65a2c@|YjFFYnfnpEhTrJ6Qq!ocOVZ?3q{G#o@NK>EX>K zh|c`$O>k-H&360xtYs(TJSP%a5U7>sbAdm{A9nqN=YnOtu@n|wZ2ZKG)XnqNWBLK~ znf?a(IHSU#b48&SOTs^|eR-)Q{Cq)>5$s$!-ntnc+G(x;y$p}DxqhD)hJIg{_)~S# z-3E$TTb5lH%V(f0YOF48rlV+apkZ~QcXMHKcWr)m;{coB)bg91!i9nB^%Ns^aCm3w z;qwyWz!L~e<#fMVhWlA|$RWUo3&mlVOCzpu2w#*(f7OuqMS0Yfl8DRf&OP+3HEc8{aj&*F#4R0;;0`tL{-Oa_< zTXU~BpEhMK?`;0g`06_Z?=kve1^Kfw{O26PA+BbSpF$N#OF{C$1W zHx;p8b7PD-QGl<@W4^75`>`(h=gy3Kt+Yp-+2&n&sJ=pvAx`LcZPIjW&QfpL+Gyk2 z6hvcUhgT^du&}$?vNA;-Yk9Z7&%2pfXjyP#d~^BJ;_z>)6aGL=!gsZaf2&RUp`Q3t z1L;~L`9>@CW(Vz`O)1yviGY7Lrrc^yz28QA+{gH}E7!c6Vco}a7%0Mya6_l+5*FHN zi`@mwgVk#j9UF7Q+sm`N8w;;DK^ri)yAjl1{&sJVCx7@Mgj|wiT+=@|5oV9K!@O@a zQT{~g&F0iwEz~=$Y4_S_CT$tNv}Zo-&N1!HHSNiL+(m!dm22I@L=6|C1`2Hl3hjrB zUB&0N9{YVLBl!G4UxQ&-%#PX$jRM*zRC}e|IlG|mAL)A;83}X|7<-7fNPTd8- zXVY8o2l@(Z`wAeVcKrZ01lDn&2t81Y87g%j;dqZ$_)XUMj8%G#Rd|k9c#l^GOxA|Y zG{nrck`_9%mir3VhO0Iv+qdQhcb2DLZ`7?$6fX?Gv{(~gKSVryx98bk3Va^JoDaYb zb9orydXAKPjh6Ei#;S1R)xi_Bp)(E822S!2G*uTm-9VUaj$3FaFLh-Tcek!j^InZ!uU#2;87xhhX-%4K`!}=`=i140 z9Vzpj)TM4{!6wdk5EnX0i?33ay3&?=vsU{V>%&ExW5pX|sk5E4ui*(8jrjW(Y@JiB zLE|-Na^Y*cYPdMu%V}DZJ50_!d@R`tPWDKI-wHs510K05;GM& zLVk1aP3PuZ{n}*B+C=TzWc~VN%f`&W_VU|(cmbS^kc4sYfwS)+4u3L?3lM^bCbaNM IzVPV3042x2tN;K2 diff --git a/admin/win/welcome.bmp b/admin/win/welcome.bmp index 8fc4078bf76dadc2352b1ac84a354af657c692c1..580504f50354f345e789f921266ebcdd518037ff 100755 GIT binary patch literal 154542 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 literal 154542 zcmeF41$bQ7weRnXNzBX)mSo8gPMfy3ZJ3O4%(j@BnVFfHnPpkFWM;NQ4m543P0}zY z_x<*a?e@iPpPkrg?kDM6_BV6poPG9M|HXTsncqFpyWx)i5`TB`&xU`^{JZb}{MUc| zpa0U~mGp1F(trJz_;=;+f9C#XhXkglr)LMFb6oz<9M-c(3Ffe#!+Q2eVD@l2XV-IB z&mIZP9!}@%dJgN^BZ1k&>6~59VLf{!Fnc(iv+FsmXO9GC52tf>J%{z|k-+TXbk45l zu%0~)9iL*~96aUC&`Xdn7P>IGwZWIjm=o1ZEGX zb9Oz4_3V+r?BR6IuII3xJrbBboX*+x9M-c(0<(wHIlG?2diF?Q_Ha69*K=6U9tq4I zPUq};4(r(?f!V|9oL$dhJ$ocDdpMo5>p84vj|659r*n2ahxP1{!0h34&aUUMo;?zn zJ)F+j^&Hl-M*_2l(>c4I!+Q2eVD@l2XV-IB&mIZP9!}@%dJgN^BZ1k&>6~59VLf{! zFnc(iv+FsmXO9GC52tf>J%{z|k-+TXbk45lu%0~^fBof`Uw;1i=bwD?$wwc3 z^!LC2{k`|z`|Dr-`p!G=y!F;wZ@&5Fpa1;lH{N*TPm;gaC4ao+2*>~Om%nfhXL3GQ zeel5tAAa~D<9znnXJ35r#aCZ_#rWTT`z<$;-tm_&nSV9I|8;haz!5vbm*NW|AAkHY z0R8Q6e|!7wx4{l1FI~Fy!V524xNza@*|VolpFVNo#PQ?Dj~+dGMDlm|@L}l-j&Ph$ zPn|l&nVip6FTecqYp=bA8lV86|Ni^$qdcE}`YE_`M{xh&3;O>Stp6Ee#E#uzyx!jy2NSTXhm#E2coyMO=wUAuOHNM~nf zV`F1wWhHoJXJ@CTrY0mLL`O%5g@*@+gaif#`Um*?`uX|z`g(bbKQGCjc;(~6LH~e& zz@Wg8kPyy~ijIzpi%UsK$;!$qC@3f|F9-Ve_I3;(FR^dmKJ5Mc`SViHQ8^+zLFAv! z{Fj65wKMqzVl73P$cPN3hD_pwqb8VQUk45xKTOXxM|~a8#k`%k{~s6 zlEbUf{8~Bx5{~?$u>Jwc$Ph`s`s%9~GI`YQ-Mb0CI9MP?_}FhuObqy9oo;S!+qZ8A zS)g0HcI~RwtDjl9@~Nkve)6fOo_O+!$DerOu_vB*6ngyeM;?ES{{=lE{^vOK#FI}x z`P7q~zjD2+&Q@;d@md@dqfVI?|sc4AHL_jQP^=7yF}ii@)~#H*Qi|`x4?p}zkp06Se*MTF?t1!R`;CvA zJ3nRMyIMDPlXjMiPO+Cxb%1_zxPE(tL1(m4f4o6=jDCBhertqoZJ=(6k4}!OZsHch zz;$MBPup#N;?7l%KJe%tfB%QyGbSTH`Y0~p33LgMfxicIPft($36Y(!j+v7UlE&lM zrHE4+psUh2E=Bwo0{4FwYpLcF<}d`xWU?>fG9DXS24xB(q9pQ+jg2M6a(8#%vSkZa zCWROX9(w2@@V)PehwRrsYUKX3X8b0Nav$BE7?a&uRwv5rUZ``p)a3YTi_`0EPOr5s zy+X@gYlW`9dilZ2|2yi3N4k=tF6mkZCx%QUMz(q$ED{;&*wreXArwrnrtpq8&79xwS}vt zZ8!9I+Hu{ZzkNu6j+)_5aPOEo=^Tk1jvd6w+ezBdQrb1tgA`~q;&AB%reA`A|3|Qv znltQ6&6(l_L!c8vAA_8SxQxffmZc~oPr@;>#jJ?GcddHF*!^kMjO{wTF&4*57QfPH z_iFP$ll`mB_OA-Wm%P^E2w~R}TKY=MG6^kzr3I3{=AiUbPUB=Q;X*EFkbfQ%+$~R6 z=#M0-7P#yCtaMuY=Y1ONH4mYOr3 zjp$5V!<;Fm=!cNxP)3n`B_<}4df_2K8QVdgNcA_5J!G@x3H8{GTAh*R#{|TSUun87 zfs6es%?__NFL||DAPCT}G%tOndD$gF%P%$G`&v813+WL);soiLT*8H1&LGzvoP#(J z-GO-3^lj!YPv8H?f=!Q8{w_*?KQ)D%?MCe*z6lA$1*u-T#_WjR1YUr~{t=iAzK%UJ@^|miJ z+#uP#+-QHPk;IIQ%kff^lK`}7*-K5!Uus(MQqz5}v_rh$EeH9C6F7~Nxr7V3{046X z;^xOoG+IJ+L!Z5C&0`d6#2WGna!9IvdUqH&Jw4j;L~ybZQgo?5|Br<4pABm%&;)3h zg=PFU$u?e&=u8fVIa9r26gWd%B4w|!_bRnYUz0=m)-TrC+zi>iSiktCdb^hz>|bhF z@=~MYON~x1H7exgL9 zIYcjb-MuSG+#aPs!=)33FmM_IxGYv!D9Z5Xbn=N@#IK(}KmU2K254BL0D(=Ig=HAW zZ&RGojG~?*w~?CjA0B(eddE|0MIOfca;=`Pv3kDtrpWsFTH)tksIz;a-u}ggB`-EO zzS!vWLc>xCvfz8?^9}c1YJ+$og`1B!!BKKDmvAAMGYCW7)U7$$^k|WKjlZeas^2~- zN^%UGzAJ58Tvl;$G5(x{jTDr+lJNcW!~HX0Ed?6ZD6Gg*2+(`>?4foh4Wk`_uaib+ z%=ylBk831s(i=~SB$c=WDE=ud#i;mg3s}h599cC;@tz#G=9XE`fC` z#0%aE)0UjTX`IX@T*&1NA~a2md2`cX&W6)js@cvr0Do_l26v- z2;bBlq$^}=m;mzO?`O!?Za3C&Ax*gf8hxg-MEOjBhH==(ZM~uZwv>?7g=*^y)wUOF>@L0-QvJZ0%=;`?g~J1EA4oGBFkoSa~Es!#+(gl9_wgJ)xGgS#MXG`PmBd zb2mg5=PLg}=PNBQQ~MikdIbes9T0&T&Q!rP|Jmj&s8%BLrKSEaOqvH-dDUhQ*k4+ zH9b|T-WF!$zviJQAE$~We6xDO3LWbvG*p3`^__pV?%N9YZNmBoK;ycxXxtY-vuc2b zlehfw=_m9<)@k-eo17{&IsNa*^mM7|>9VV2cBTx%Pg+RG@=Q5IivzMgQ(=3i((Y`P z!`W)bb2ZD(*DgO_d*}JuyU*9%d%phu^Yss$Z+PH^7D4A5c*{XP;sj1xdajmBxRA>k zgrTHkO2_A3+!2!A{pw^SFaF)+;ZA2FnO^;at3_Lta*WcF>ia6(QfDqr)sWlmgZp-1 zEd`qLibX4W!epK#BvOl(0*!`0xaLXxw(exA@yU{ZLnbFnBvfj0vJ@ME%p_!fs?0({ zmZ!?B1eHV9r^;b7^a@ zYvWU7YyS*4&YW!z^px;^_`us*ci(cX0h%@-4HN2U`kyRK(l{bUla^cs`o0a%sN^{7 z?aeYeUi5Fs_;|6P6M{@6MCN8DA@h?Z7G!ZJOD#{9K~^WrtWTENNNDlNa{H4NOHNfT zJzcf@boGkUHFuw>x%W)%{b%Zab*BEovkebC-vaT1w;bdnPPp@Q4JSLDuHr)bQxy!t zXp9Llxa2OS+>SaLVh+p%Vph`WNvCRby!hX+V{kZ6slvx``$}?ADcm@7VmGN80geiV zf)oR}eQ@7$tO1&?FU>$U8?Z}<@mbzwErX?JVl=LM#nzR|C7${Ratw|ZT$c=w78o8a zgp8nLf{c$9nMla=SdrQBVsk+yEPPlVFR?maVl5$?Lc)I?#XX=0Td^5xg-g1zSIDykRnM=5k%Nc~B7|>dfxC{4! zxGA@04iJ-JR?H2O&XWmCXATS3WzFDlzDh%&jmPR|R;?s<(^#M_jK{<8QHo+37zk!^ zn|kKA7Hf=}3X8;&-tS8}Zks(IShZMV3d4A*-Xs)<=tNBxHNEWbx4wyJMve$IG0K zmoGa}x#C3S-6yN=Jz0JKsoGzks{PIBy5E8I#b$^XyyYMtal&0Et2lYti3-Q#vH@MJPaE8W;#>B3&Z6<$?PaVl55R1Ra_}atp?%2#)P zw(h~)Yf0~5u7vXR4&@;b$ly?(;h}sZ=y1OA;e6A>1*V4!%?=luA1<^wTxfZ?$m(#B z_2D8L3E3VgT70;8@sVQtBPC0YmM%S3w)}YcoyRNhIZ<`riRuSV)I4~q_P3|%{`++O zgBM#MUi{{iz?zRZ;qDWaoV@&4xzn*SE@u#ivOQb`F+O*JxSgfs)=YqDFd37>1x$?T zF;9pIOJ@!XdIzEFuqhZnkfYKbYT>tb)tc4#I?7LWG+`8&1_pBbs<2yy_0=#Ym2$%y# z!Omp1P|{MnO?lca#QJKOW(PI-7-5LEKiy!Og!gP;tx)czHIt#eFYCG_bYP#5iBvdx z`*Za7=NKHwF+7k+3{b+EwtK!MG{0^0)xiw_pq2`aQd zSm#(_@l`WFV4h>s{eOs5DId z3zoI_W-2uY*?F&{p@ccGYR~fvEaFq2vSe^IaNj~teoL^HdT%O3{0|Ss;EQN?laJ9e zAp`mCmNklnZkl`2!1}tRJ(CIJkdB}%-Mv|Q`?4rf4EN<2?aN^igq32m{ki7*b1nAg zS?kKKyvq1IMc$JW=!8 z6Se<+vhLUCn;>5BmV*x*7e4%+lhxw185~-CKVat!w6Fl{Ce3ld%~^96&~Wt@YGwSLQ8*J2J7-Q;N$EG!lfxLNbP z<^8l`wLJl6wGFSKFVe#(K@DN zwlCLopO~c4-W(>(w3)mvN{a#ul5hu)(;3%gUvqbwVwvZ!wyz~mqf{Yq^LP=fz4X&a zdq{q6VNU+jv6klE?4Tou#CuadP&2Yp$^Pxt8`f(jY*rhM*VvPGEn(@Jd(t7TX+hf4 z89LJ$y3-kYGnoc6S%x!NMl;zad$Y~R1)%-+QF;o+A}^9RcXFWrs_b9x5ROI2@#6|72Z21Cx<5grp=qpLpLb*M5s@tTuT~L zse*Q=Y6?oz+MTAoJ6&f_y6&C~{XLn6)0sxoS;jM2rqfyG(^(eN*;dop))KOr&as`& zu?NSKmAF~bW-N{)QWBq{1~Yl~dj;Z-Ailq7+5X}c2TJZbSbF!tvilB|-z^I2+WQYx zfd1Ww%kMl?w)~Jl+38Tpl7qz#2a4?W7cOSZeZZQ(7}}5P5K2W{0CD?+MYenMZ1(0_ zLo+#+GdUJB*=94@rZd@WI%Vbt)0z5vGGGLDU<%e?5KaY6rCuuvj;M~rX(w!1w|OI5 zQdu0qlYRpPzx^nOmEAwX?1`C3Ag)pw;p z8iG7FdJ-B}iUvMl#xS?$TP-koK$CwuYU ze5Yd-%T8509w`G;o88&gg0ie6WGVTdBQ|?-7f5BR-a6{Rv;Wv z?hdyM-H0b=>60B<>|-G(r*}$Zly+Bsx@rFutp7PBV>1;)@pKz~Keit{=Db0%$X#_J zQFUk1wWPN5vH}3Q#$<}-t`x0ZsXDvBI!%8n-C%dR(e4bBJ(*^^GtGBrTJFxY5(L)S z!oE&bIUcLPS6feIT0y%qEO%vC?8-3Tm2NgEpBSnbJ1AneNGC+Dsl6^rq5bM;kR3%A5k5Fnby0>&XNu$=CQk;IV-< zeG1ju+FGI^F_A6DBx$$Aw0|Pj7&X-a1vM!dofEd2pq86M$AR2K!FOw~e}QEAj$!m`kO*uS1+RL0_!bDdZ2 z*i7$~I7oMeB#o{Veezpk+CLiWt42*s;TZ>ZQt_yAQc@D7qEpZ&g|=Yjv4m?$Wh_Bu zJP}eIPgEOERG&!Fm`Ku^NY>t&!kU-<16w|R3M1x@{!C)lOa5Ra7CSxgP{MOZu{=_U33>8AZtu)b>4thp0ks23=xpYqtMP~xF95~no!LsA}% zQyz_19*akTRmT$4#uL=X6E(*L);c?r^>(Hh>`XP9Of#8G188%JI5dpezHJN4t zP!U$7i2-`D1aEV?hFlJ;QBJTnolG~LOhak)cc#J=tZAXr<4G_J%P=nN7eW-*hzOy{ z)%pF}SFCZfGDxD!1B|!Iw0|_#)P1Di7&Yml)TrtFxO#dRByCgdjZhkiy_S?m;slMx zsf@;{j>fBvC1{K#YK|pok0#x(RMQgp76?#@&oLkYpoXfoXd zC?$Ye?8-z*r16DGQW2n&cp+32MU@Pg3Ofu1YcQxemZ(0Ks5Y7a)-4mtR9ih<`uHG4@))}hW5u(-+qTU&**%_|Y6`|7;sn;8&-xqDz7h^mSXEqpb zK9p!TLw=rbfw42-ShDe0ve8(w!FV!r&>c(C8B1bHnv%Hzkk&Y=n5@I66DdL)Q3>IB z(lK7LL6HPXXz{)RoVneBA`6c1Oha8^#AqUgVRgsB8kJ6jSy~P7ei>z?F&ukcLCLoV zJ4S4inl?L)iP|*DrBNHn|LBDL$6<|8v#QQUDb{5efz~(86K5Y!)$AP#{gDcT(btmV zPz|+BgrPi zNuZ3KVy(Kppd6{$6`|1)rq&*!+7_hJ8l>D3sMH*w+#IOV5~$W1q|p|l*&eFh5vJ1_ zq1PR0&=Y0U8)MoRXS+Mwc2ADkPy)s-1{_Y%A5PF4PS6=n&>Bh59FA8Xj#C?oQyq#` zlS~qfG)EJ)C2D{^=#NYM&xG&>W;;Qa6fwh+e1N3lWYyyRg(#}oL@JDsE5ekXRDV#O zk$AO{co>I$rJ-1on@A zsY^;qSeUyfe1}|ffc!w@wWKf*r7#$!C@5Ow=ZJAAPGdMuYb0K0BtdU9(O@(Q;{t25 z@f4CS+E%2vyRZ$HtbsBGf^-Ekr=v9H7+br`X`D(BPv` z@6G>84Zg~aeyU9Y>dk?gEkW9?!8+}s`W@khT@l9JQ8tqqHoLM+`eTjz(JH*X0=ni%g4SrF&S)a=VX#J|{Sr*gB((tgX)~P* z+_ro3VZn4f1$JOcA7w^W(4rBcMU19`&JRXQP!?iAIM+%{t~1m!etSS*0LuzIG(kp$ z3#H_d?kE3oaQ_Iq25U+HTnae@jWV{K(CTsZ^H$7vmFtVRF3I;tLJIv+iV_(jK((P* zjiETL;doul>PqH}MV4#Ez2s^<<*FeMNTJ44vDRC;&PTPuPrbokvnfEkIY_rPSiddQs3XE^JdI#&+!Jls z6Rl5J?~5iDX!S*F^hc}pN2~NjDfdMx_C+Z4Mkw;4KT@e*;4YmrU}5+Gr#q6UKbmA9 z{C|qcSgI+`hn#;r)n-~)w>8BY$0Y>=W|UZDW; zNUu*KxBh6=ff)6{Sk1vW?ID3RRgw{yjwG3lCR58$UXkCLNzB<~DDeu=SIs$8wJlhs zIZ&z5U!mStpj_=CTka}b;w)3RfFSQyUjXGbg27jH#0KMiQ zgO*TBk)fp+bw(O=Md)=!>UKwJcSmY=N2>Qks`fj{$=6fOZhLa8rO zxj#x}AO^62Omiq+8_!RnN4ZBuFbd-2je@Xibd}mrl3j>mkQkxM`9tye4K>`_K#X#K zv|@i00zoVS=-!BHEwcW{7GzKd*k zm~78=NUkSbt~Xr1S6GS?Rs+`k(Hat;sY4}EZmG40spUQU%xTPVlcsCDAAxbOusEmw>?a!BV4N^OoPF@!d1G$lsdx{ zIz#0;Lj-k&3hEA(?+#b!2^aN6Z=`Zx6!TXf_=m3QQxs4dkWq{#Q{Rz8(ym`}q>L9R zu#u$d*8}UpIL(1r^??{JXAqnQ0zoVY2N7LYbeQ(WU?0{x`ML&*FETV*rayW$cH7J!8$eH7D!W+tn4?@9x@GnGTouql2idCHX;lJS>Yscm6`)F+LTL!@%oYk$z(VY zd>sz6A*2MqEeX)1VFGA9(y;C*fkk_$a!Zh6lhm96+C!$o4O?DRvg6r_(pSFvpy=GB zVX5b$?2R&cTjUD2%NOlXDsfgRbyY2MQ?KyQtn|{Z_R*{JGwX{n?~l`O4%TZ9)@cdW zZVl0F3sG+iRb~A45XJTo(qP&4AV`M)JA!39Lu9)oxOazP?^yj6E@Ccg>`*+Gi^dua z3-5%|f-+c>oWqyMG=>tuT4yj$Yamvm{|eT3;eY03g9XL%|TD6pZtaOA$Mrh`^F6szb)O8(_=@JS6;_?Wp=L*(+(VG1N zYZ@Tb$Rxw~Y7!gz66WJ+rc(K!23IT^paDWj0{#(@U;iCC`uEq@4&=*cZj{a1D3`ZIpZnbNFMe=uHmAWpkK2CUWkqPZ4@!B@!P z45c>G6?$C(%9Xf#ga@%2!ma>Tm&k{Iv>U%!tm)LT(UBegG{AA-WQl8{{T5YwE$Rrq zF3EI;K(bw-@?8@7^hBukMyk{LAS3FJ)gK@ejyE1mpnL{sf;FJ?mV^Hc^i_)nXxSPm z(5?$hofj4Z^wtG=o7asLzW(*!rS#fYAJkr)wkq+FO$ZSYrV^j9PYX!4hB@?X^CC#V_nUnId@Mhd#b z(sA$douP_2{O)iS*paBN4%y=Xs+&OEF}|X`4o6DyQAPvtBDbU=fMTE?y-_MXkxD%g z3fAC`3)adtMycgLoZd_Vu+YV51SstURk?o94c- zC17ED(6uC;yiA9Pee#_Wth*!BdLlJ?qqO^?b^Bv+(a6DMFo9(z8WB>Ug=C}_-Gl3n z5I|#YO#uREEV>$t7C-@Y;^RqXs8!CAH>kt2#PJi^~j?prejE#yJ8n*N4d^dkz|j%)cY)|_gPr) zy|BS&VWaQDCclMJ(1oQ3E&}v6;oIdqLWt~2U10zRe3}%4l!X0p1_SYwhiEL;?Qp0B ze+5T|1S8S>iP7qfR_}>Y?TKU&6X$?fh!HpqA*<`j5UFbBx<$vuvhqcL@@h5?7B}n0 zZx(BEenJL;gYZEH?jIi|U*f)?E%3U;Jej{td$3%`WkXN_Ym(j`Ne{CxMuK&maCb+` zFm2<3cnQ$4dL&~2-4&tM0novUlOvZ;0zIb<-?$*C=-0slb0y{J7{YAM$jW0mKI_z;>H>{sED0hU63 zTvO$yuqIfumP+Q&BU)4etYAKp?7y(qdqHc!wY0D`aA6x(ATk`e_7H^*Arn=ancb0E zU@dCf7{k6;@MRAGO(*02cq5Dgne@i!kac%Os&$01u~xA;P`=Smw$4YU+H+y0`+_nT z3DDc-=WU*!vk{=@r9Hd0Kko-XUwwPwleYtR)oW#LQO($>mc2zIXPbVtk3p@UW}&l2 zk&Ak<3!z-4%uT7>O|jfvzQSFu(p{#~og2-sa-U!Av7iRfy%yAYUk1J5GU8I-E=zE4 z3s&d|QR=wD8gTc<&)onPuazi8XM{4MixHqFa=ZeIGc^HSEX>(6`Rn-BjL{pp(zN8UJne@(b*+D7%P zE&5eHkVft{^}KDW`P)?rcPJM*D;7H|l(@*1y2zHgEGlzFqvn^p%&TynU+E@5ukn~) z>p8#HYkr;gd|~OnQZol}vN*ZcAcc0|4uv~)!JW`#T$6!>BXV*W#Gf0W13l3~jj83q zx-*uZB`Xj+1NsRuK; zE-h&BXXcAq0%co+sA8yPROo7UhST1pccIrE4e#Jfq!tkkvGm4r1faFMA~icBMB?2V ztk4u7+u*yX)@xyv$ASvC`9M&-V_w15d3l@X;k`4~&r4l5FJ;ZTzT7{5^U=SjOJBSf zzpqn2Ynxt~hkm7(diEB~9LKJbyHzP~n_~Vp`GW0og*zzE7Z&eWz+Fmq%qw-CSMD+)tp_Dx~U__XPyiJy+De zGeW)nviFv2^pmOcSy(NMdcMS{=l#&A*UU>^eIr;)HDTtR7a!}*)GKjU&)B4vwV61s zoV7_Yd$U5$X1UxgvUyu&^0zL`-#Q<4D%>`&X!~Wv(Xs0wE*v}g8>zA^sUs^dt-R3K2(Z7mtw8Wk_|a0Sht5NwuK-Z*_I%303-%oSD5qbeeTN)U?(

%0oLd6n+-L^Qu_;|s~#8-UwifzEAf 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 Z_!u=lz?xBa`E}(0Yrn?FsPSO^{{a58ip&52 From 811c7b8754bd60df9d80fef8371a99145976071c Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 31 Mar 2011 12:18:49 -0400 Subject: [PATCH 02/63] Make debug output a little more helpful --- src/sip/twitter/twitter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index 655dd832b..e3390608a 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -599,7 +599,7 @@ TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString, qDebug() << Q_FUNC_INFO; if ( m_attemptedConnects.contains( screenName ) && m_attemptedConnects[screenName] ) { - qDebug() << "Already attempted to connect to this peer with no change in their status, not trying again for now"; + qDebug() << "Already attempted to connect to " << screenName << " with no change in their status, not trying again for now"; return; } if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) || !peerData.contains( "node" ) ) From 85be5653e560e35e7aea8553af489df4014caa2d Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 31 Mar 2011 15:12:08 -0400 Subject: [PATCH 03/63] Add more debugging and remove the don't-try-to-reconnect behavior --- src/libtomahawk/network/servent.cpp | 3 +++ src/sip/twitter/twitter.cpp | 10 +--------- src/sip/twitter/twitter.h | 1 - 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index 0c0d8285c..a602d176c 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -727,8 +727,11 @@ Servent::isIPWhitelisted( QHostAddress ip ) bool Servent::connectedToSession( const QString& session ) { + qDebug() << Q_FUNC_INFO; + qDebug() << "Checking against " << session; foreach( ControlConnection* cc, m_controlconnections ) { + qDebug() << "Checking session " << cc->id(); if( cc->id() == session ) return true; } diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index e3390608a..1a5f91163 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -178,7 +178,6 @@ TwitterPlugin::disconnectPlugin() delete m_twitterAuth.data(); m_cachedPeers.empty(); - m_attemptedConnects.empty(); m_isOnline = false; } @@ -571,7 +570,6 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q { m_cachedPeers[screenName] = QVariant::fromValue< QHash< QString, QVariant > >( _peerData ); TomahawkSettings::instance()->setTwitterCachedPeers( m_cachedPeers ); - m_attemptedConnects[screenName] = false; } if ( m_isOnline && _peerData.contains( "host" ) && _peerData.contains( "port" ) && _peerData.contains( "pkey" ) ) @@ -597,14 +595,9 @@ void TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString, QVariant > &peerData ) { qDebug() << Q_FUNC_INFO; - if ( m_attemptedConnects.contains( screenName ) && m_attemptedConnects[screenName] ) - { - qDebug() << "Already attempted to connect to " << screenName << " with no change in their status, not trying again for now"; - return; - } if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) || !peerData.contains( "node" ) ) { - qDebug() << "TwitterPlugin could not find host and/or port and/or pkey for peer " << screenName; + qDebug() << "TwitterPlugin could not find host and/or port and/or pkey and/or node for peer " << screenName; return; } QString friendlyName = QString( '@' + screenName ); @@ -614,7 +607,6 @@ TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString, peerData["pkey"].toString(), friendlyName, peerData["node"].toString() ); - m_attemptedConnects[screenName] = true; } void diff --git a/src/sip/twitter/twitter.h b/src/sip/twitter/twitter.h index 4e8a98a31..c0da00d7f 100644 --- a/src/sip/twitter/twitter.h +++ b/src/sip/twitter/twitter.h @@ -108,7 +108,6 @@ private: qint64 m_cachedMentionsSinceId; qint64 m_cachedDirectMessagesSinceId; QHash< QString, QVariant > m_cachedPeers; - QHash< QString, bool > m_attemptedConnects; QSet m_keyCache; bool m_finishedFriends; bool m_finishedMentions; From 48f69c3180f735cae6ffda778bfb3eb536bbf7dd Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 1 Apr 2011 11:52:15 +0200 Subject: [PATCH 04/63] * Disabled launching Tomahawk from within the Windows installer. It caused starting Tomahawk with admin privileges. --- admin/win/nsi/tomahawk.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi index c2d2a9bd1..e9ba96112 100644 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -9,7 +9,7 @@ !define OPTION_SECTION_SC_DESKTOP !define OPTION_SECTION_SC_QUICK_LAUNCH !define OPTION_FINISHPAGE -!define OPTION_FINISHPAGE_LAUNCHER +;!define OPTION_FINISHPAGE_LAUNCHER !define OPTION_FINISHPAGE_RELEASE_NOTES ;----------------------------------------------------------------------------- From 70ddc32e4204354525d0e516a8f7cf013c2484cc Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 2 Apr 2011 10:24:20 +0200 Subject: [PATCH 05/63] * Only keep track of the 50 most recent tracks in WelcomeWidget. Don't auto-resolve playback-logs any longer. --- .../database/databasecommand_logplayback.cpp | 3 ++- src/libtomahawk/playlist/playlistmodel.cpp | 10 +++++--- src/libtomahawk/playlist/playlistmodel.h | 1 + src/libtomahawk/playlist/trackmodel.cpp | 14 +++++++++++ src/libtomahawk/playlist/trackmodel.h | 2 ++ src/libtomahawk/widgets/welcomewidget.cpp | 24 +++++++++++++++++-- src/libtomahawk/widgets/welcomewidget.h | 3 +++ 7 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/database/databasecommand_logplayback.cpp b/src/libtomahawk/database/databasecommand_logplayback.cpp index 9254ea979..6443b2216 100644 --- a/src/libtomahawk/database/databasecommand_logplayback.cpp +++ b/src/libtomahawk/database/databasecommand_logplayback.cpp @@ -44,7 +44,8 @@ DatabaseCommand_LogPlayback::postCommitHook() connect( this, SIGNAL( trackPlayed( Tomahawk::query_ptr ) ), source().data(), SLOT( onPlaybackFinished( Tomahawk::query_ptr ) ), Qt::QueuedConnection ); - Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString(), uuid() ); + // do not auto resolve this track + Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString() ); if ( m_action == Finished ) { diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index 8e4d24ae6..2ab23bf51 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -396,12 +396,16 @@ PlaylistModel::playlistEntries() const } +void +PlaylistModel::remove( unsigned int row, bool moreToCome ) +{ + removeIndex( index( row, 0, QModelIndex() ), moreToCome ); +} + + void PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome ) { - if ( isReadOnly() ) - return; - TrackModel::removeIndex( index ); if ( !moreToCome && !m_playlist.isNull() ) diff --git a/src/libtomahawk/playlist/playlistmodel.h b/src/libtomahawk/playlist/playlistmodel.h index e69bf59e1..bc743a18a 100644 --- a/src/libtomahawk/playlist/playlistmodel.h +++ b/src/libtomahawk/playlist/playlistmodel.h @@ -62,6 +62,7 @@ public: void insert( unsigned int row, const Tomahawk::query_ptr& query ); + void remove( unsigned int row, bool moreToCome = false ); virtual void removeIndex( const QModelIndex& index, bool moreToCome = false ); signals: diff --git a/src/libtomahawk/playlist/trackmodel.cpp b/src/libtomahawk/playlist/trackmodel.cpp index eceadd401..e3fd2083a 100644 --- a/src/libtomahawk/playlist/trackmodel.cpp +++ b/src/libtomahawk/playlist/trackmodel.cpp @@ -26,6 +26,7 @@ #include "utils/tomahawkutils.h" #include "album.h" +#include "pipeline.h" using namespace Tomahawk; @@ -370,3 +371,16 @@ TrackModel::onPlaybackStopped() oldEntry->setIsPlaying( false ); } } + + +void +TrackModel::ensureResolved() +{ + for( int i = 0; i < rowCount( QModelIndex() ); i++ ) + { + query_ptr query = itemFromIndex( index( i, 0, QModelIndex() ) )->query(); + + if ( !query->numResults() ) + Pipeline::instance()->resolve( query ); + } +} diff --git a/src/libtomahawk/playlist/trackmodel.h b/src/libtomahawk/playlist/trackmodel.h index d9cec03d8..b3a5e0c24 100644 --- a/src/libtomahawk/playlist/trackmodel.h +++ b/src/libtomahawk/playlist/trackmodel.h @@ -76,6 +76,8 @@ public: virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } virtual bool shuffled() const { return false; } + virtual void ensureResolved(); + virtual void append( const Tomahawk::query_ptr& query ) = 0; PlItem* itemFromIndex( const QModelIndex& index ) const; diff --git a/src/libtomahawk/widgets/welcomewidget.cpp b/src/libtomahawk/widgets/welcomewidget.cpp index cc4460895..d7ad62938 100644 --- a/src/libtomahawk/widgets/welcomewidget.cpp +++ b/src/libtomahawk/widgets/welcomewidget.cpp @@ -31,7 +31,9 @@ #include -#define FILTER_TIMEOUT 280 +#define HISTORY_TRACK_ITEMS 50 +#define HISTORY_PLAYLIST_ITEMS 10 +#define HISTORY_RESOLVING_TIMEOUT 2500 WelcomeWidget::WelcomeWidget( QWidget* parent ) @@ -46,7 +48,10 @@ WelcomeWidget::WelcomeWidget( QWidget* parent ) m_tracksModel = new PlaylistModel( ui->tracksView ); ui->tracksView->setModel( m_tracksModel ); - m_tracksModel->loadHistory( Tomahawk::source_ptr() ); + m_tracksModel->loadHistory( Tomahawk::source_ptr(), HISTORY_TRACK_ITEMS ); + + m_timer = new QTimer( this ); + connect( m_timer, SIGNAL( timeout() ), SLOT( checkQueries() ) ); connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) ); @@ -96,10 +101,25 @@ WelcomeWidget::onSourceAdded( const Tomahawk::source_ptr& source ) } +void +WelcomeWidget::checkQueries() +{ + m_timer->stop(); + m_tracksModel->ensureResolved(); +} + + void WelcomeWidget::onPlaybackFinished( const Tomahawk::query_ptr& query ) { m_tracksModel->insert( 0, query ); + + if ( m_tracksModel->trackCount() > HISTORY_TRACK_ITEMS ) + m_tracksModel->remove( HISTORY_TRACK_ITEMS ); + + if ( m_timer->isActive() ) + m_timer->stop(); + m_timer->start( HISTORY_RESOLVING_TIMEOUT ); } diff --git a/src/libtomahawk/widgets/welcomewidget.h b/src/libtomahawk/widgets/welcomewidget.h index 9ec4e249c..036e3d5ca 100644 --- a/src/libtomahawk/widgets/welcomewidget.h +++ b/src/libtomahawk/widgets/welcomewidget.h @@ -124,10 +124,13 @@ private slots: void onPlaylistActivated( QListWidgetItem* item ); void onPlaybackFinished( const Tomahawk::query_ptr& query ); + void checkQueries(); + private: Ui::WelcomeWidget *ui; PlaylistModel* m_tracksModel; + QTimer* m_timer; }; #endif // WELCOMEWIDGET_H From 5cde25cefed51a6a5cec13303617724764dfbca3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 2 Apr 2011 10:24:48 +0200 Subject: [PATCH 06/63] * Properly thread QtScriptResolver. --- src/resolvers/qtscriptresolver.cpp | 126 +++++++++++++++++++++-------- src/resolvers/qtscriptresolver.h | 39 +++++++-- 2 files changed, 125 insertions(+), 40 deletions(-) diff --git a/src/resolvers/qtscriptresolver.cpp b/src/resolvers/qtscriptresolver.cpp index d4c0cc5f5..a19147b14 100644 --- a/src/resolvers/qtscriptresolver.cpp +++ b/src/resolvers/qtscriptresolver.cpp @@ -27,19 +27,86 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) : Tomahawk::ExternalResolver( scriptPath ) - , m_engine( new ScriptEngine( this ) ) - , m_thread( new QThread( this ) ) , m_ready( false ) , m_stopped( false ) { qDebug() << Q_FUNC_INFO << scriptPath; + m_thread = new ScriptThread( scriptPath, this ); + connect( m_thread, SIGNAL( engineFound( QString, unsigned int, unsigned int, unsigned int ) ), + SLOT( onEngineFound( QString, unsigned int, unsigned int, unsigned int ) ) ); + m_thread->start(); - QFile scriptFile( scriptPath ); + connect( this, SIGNAL( destroyed( QObject* ) ), m_thread, SLOT( deleteLater() ) ); +} + + +QtScriptResolver::~QtScriptResolver() +{ + Tomahawk::Pipeline::instance()->removeResolver( this ); + delete m_thread; +} + + +void +QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) +{ + m_thread->resolve( query ); +} + + +void +QtScriptResolver::onEngineFound( const QString& name, unsigned int weight, unsigned int timeout, unsigned int preference ) +{ + m_name = name; + m_weight = weight; + m_timeout = timeout; + m_preference = preference; + + qDebug() << "QTSCRIPT" << filePath() << "READY," << endl + << "name" << m_name << endl + << "weight" << m_weight << endl + << "timeout" << m_timeout << endl + << "preference" << m_preference; + + m_ready = true; + Tomahawk::Pipeline::instance()->addResolver( this ); +} + + +ScriptThread::ScriptThread( const QString& scriptPath, QtScriptResolver* parent ) + : QThread() + , m_parent( parent ) + , m_scriptPath( scriptPath ) +{ + moveToThread( this ); +} + + +void +ScriptThread::resolve( const Tomahawk::query_ptr& query ) +{ + m_engine->resolve( query ); +} + + +void +ScriptThread::run() +{ + QTimer::singleShot( 0, this, SLOT( initEngine() ) ); + exec(); +} + + +void +ScriptThread::initEngine() +{ + m_engine = new ScriptEngine( m_parent, this ); + QFile scriptFile( m_scriptPath ); if ( !scriptFile.open( QIODevice::ReadOnly ) ) { - qDebug() << Q_FUNC_INFO << "Failed loading JavaScript resolver:" << scriptPath; + qDebug() << Q_FUNC_INFO << "Failed loading JavaScript resolver:" << m_scriptPath; deleteLater(); return; } @@ -48,43 +115,32 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) m_engine->mainFrame()->evaluateJavaScript( scriptFile.readAll() ); scriptFile.close(); + QString name; + unsigned int weight, preference, timeout; QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( "getSettings();" ).toMap(); - m_name = m.value( "name" ).toString(); - m_weight = m.value( "weight", 0 ).toUInt(); - m_timeout = m.value( "timeout", 25 ).toUInt() * 1000; - m_preference = m.value( "preference", 0 ).toUInt(); + name = m.value( "name" ).toString(); + weight = m.value( "weight", 0 ).toUInt(); + timeout = m.value( "timeout", 25 ).toUInt() * 1000; + preference = m.value( "preference", 0 ).toUInt(); - qDebug() << "QTSCRIPT" << filePath() << "READY," << endl - << "name" << m_name << endl - << "weight" << m_weight << endl - << "timeout" << m_timeout << endl - << "preference" << m_preference; - - m_engine->moveToThread( m_thread ); - m_ready = true; - Tomahawk::Pipeline::instance()->addResolver( this ); - - connect( this, SIGNAL( destroyed( QObject* ) ), m_thread, SLOT( deleteLater() ) ); -} - - -QtScriptResolver::~QtScriptResolver() -{ - Tomahawk::Pipeline::instance()->removeResolver( this ); - delete m_engine; -} - - -void -QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) -{ - QMetaObject::invokeMethod( m_engine, "resolve", Qt::QueuedConnection, Q_ARG( Tomahawk::query_ptr, query ) ); + qDebug() << Q_FUNC_INFO << name << weight << timeout << preference; + emit engineFound( name, weight, timeout, preference ); } void ScriptEngine::resolve( const Tomahawk::query_ptr& query ) { + if ( QThread::currentThread() != thread() ) + { +// qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; + QMetaObject::invokeMethod( this, "resolve", + Qt::QueuedConnection, + Q_ARG(Tomahawk::query_ptr, query) + ); + return; + } + qDebug() << Q_FUNC_INFO << query->toString(); QString eval = QString( "resolve( '%1', '%2', '%3', '%4' );" ) .arg( query->id().replace( "'", "\\'" ) ) @@ -113,9 +169,9 @@ ScriptEngine::resolve( const Tomahawk::query_ptr& query ) rp->setBitrate( m.value( "bitrate" ).toUInt() ); rp->setUrl( m.value( "url" ).toString() ); rp->setSize( m.value( "size" ).toUInt() ); - rp->setScore( m.value( "score" ).toFloat() * ( (float)m_parent->weight() / 100.0 ) ); + rp->setScore( m.value( "score" ).toFloat() * ( (float)m_resolver->weight() / 100.0 ) ); rp->setRID( uuid() ); - rp->setFriendlySource( m_parent->name() ); + rp->setFriendlySource( m_resolver->name() ); if ( m.contains( "year" ) ) { diff --git a/src/resolvers/qtscriptresolver.h b/src/resolvers/qtscriptresolver.h index 05b55cc71..7c69fe103 100644 --- a/src/resolvers/qtscriptresolver.h +++ b/src/resolvers/qtscriptresolver.h @@ -26,9 +26,11 @@ #include #include #include +#include #include #include +class ScriptThread; class QtScriptResolver; class ScriptEngine : public QWebPage @@ -36,9 +38,10 @@ class ScriptEngine : public QWebPage Q_OBJECT public: - explicit ScriptEngine( QtScriptResolver* parent ) + explicit ScriptEngine( QtScriptResolver* resolver, ScriptThread* parent ) : QWebPage( (QObject*)parent ) , m_parent( parent ) + , m_resolver( resolver ) {} public slots: @@ -54,9 +57,35 @@ protected: { qDebug() << "JAVASCRIPT ERROR:" << message << lineNumber << sourceID; } private: - QtScriptResolver* m_parent; + ScriptThread* m_parent; + QtScriptResolver* m_resolver; }; + +class ScriptThread : public QThread +{ +Q_OBJECT + +public: + ScriptThread( const QString& scriptPath, QtScriptResolver* parent ); + + void run(); + + virtual void resolve( const Tomahawk::query_ptr& query ); + +signals: + void engineFound( const QString& name, unsigned int weight, unsigned int timeout, unsigned int preference ); + +private slots: + void initEngine(); + +private: + ScriptEngine* m_engine; + QtScriptResolver* m_parent; + QString m_scriptPath; +}; + + class QtScriptResolver : public Tomahawk::ExternalResolver { Q_OBJECT @@ -76,12 +105,12 @@ public slots: signals: void finished(); - + private slots: + void onEngineFound( const QString& name, unsigned int weight, unsigned int timeout, unsigned int preference ); private: - ScriptEngine* m_engine; - QThread* m_thread; + ScriptThread* m_thread; QString m_name; unsigned int m_weight, m_preference, m_timeout; From 727d8c83efc073daab1c7a89383a3fa465524162 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 2 Apr 2011 11:39:49 +0200 Subject: [PATCH 07/63] * Updated Changelog. --- ChangeLog | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index c3a791448..b25481516 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,9 @@ Version 0.0.3: - * Fix crashes in Twitter authentication. For reals now. + * Fix crashes in Twitter authentication. * Properly honor the chosen port number if a static host and port are marked as preferred. + * Don't automatically try to resolve all incoming playback logs. This + speeds up importing sources a lot. Version 0.0.2: * Don't reconnect to Jabber if the settings dialog is closed successfully From a080323e2ef677326448fda2577a236ea6e3e512 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 3 Apr 2011 00:14:24 -0400 Subject: [PATCH 08/63] switch to using KDSingleApplicationGuard instead of QtUniqueApp as it's broken on windows. KDSingleApplicationGuard is from KDTools and this is the GPL-licensed version. Conflicts: src/main.cpp --- include/tomahawk/tomahawkapp.h | 5 +- src/headlesscheck.h | 8 +- src/libtomahawk/CMakeLists.txt | 12 +- .../kdlockedsharedmemorypointer.cpp | 475 +++++++++++++ .../kdlockedsharedmemorypointer.h | 115 ++++ .../kdsharedmemorylocker.cpp | 40 ++ .../kdsharedmemorylocker.h | 36 + .../kdsingleapplicationguard.cpp | 622 ++++++++++++++++++ .../kdsingleapplicationguard.h | 74 +++ .../kdtoolsglobal.cpp | 32 + .../kdsingleapplicationguard/kdtoolsglobal.h | 113 ++++ .../kdsingleapplicationguard/license-gpl | 349 ++++++++++ .../kdsingleapplicationguard/pimpl_ptr.cpp | 203 ++++++ .../kdsingleapplicationguard/pimpl_ptr.h | 44 ++ src/libtomahawk/qtsingleapp/qtlocalpeer.cpp | 199 ------ src/libtomahawk/qtsingleapp/qtlocalpeer.h | 72 -- src/libtomahawk/qtsingleapp/qtlockedfile.cpp | 192 ------ src/libtomahawk/qtsingleapp/qtlockedfile.h | 96 --- .../qtsingleapp/qtlockedfile_unix.cpp | 114 ---- .../qtsingleapp/qtlockedfile_win.cpp | 208 ------ .../qtsingleapp/qtsingleapplication.cpp | 344 ---------- .../qtsingleapp/qtsingleapplication.h | 84 --- .../qtsingleapp/qtsinglecoreapplication.cpp | 148 ----- .../qtsingleapp/qtsinglecoreapplication.h | 66 -- src/main.cpp | 16 +- src/tomahawkapp.cpp | 18 +- thirdparty/jreen | 2 +- 27 files changed, 2133 insertions(+), 1554 deletions(-) create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h create mode 100644 src/libtomahawk/kdsingleapplicationguard/license-gpl create mode 100644 src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp create mode 100644 src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h delete mode 100644 src/libtomahawk/qtsingleapp/qtlocalpeer.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtlocalpeer.h delete mode 100644 src/libtomahawk/qtsingleapp/qtlockedfile.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtlockedfile.h delete mode 100644 src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtsingleapplication.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtsingleapplication.h delete mode 100644 src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h diff --git a/include/tomahawk/tomahawkapp.h b/include/tomahawk/tomahawkapp.h index bf83f6e86..7775a6528 100644 --- a/include/tomahawk/tomahawkapp.h +++ b/include/tomahawk/tomahawkapp.h @@ -40,6 +40,7 @@ #include "network/servent.h" #include "utils/tomahawkutils.h" +#include "kdsingleapplicationguard/kdsingleapplicationguard.h" class AudioEngine; class Database; @@ -98,9 +99,11 @@ public: // because QApplication::arguments() is expensive bool scrubFriendlyName() const { return m_scrubFriendlyName; } +public slots: + void instanceStarted( KDSingleApplicationGuard::Instance ); + private slots: void setupSIP(); - void messageReceived( const QString& ); private: void initLocalCollection(); diff --git a/src/headlesscheck.h b/src/headlesscheck.h index 189f1127e..f82fcea33 100644 --- a/src/headlesscheck.h +++ b/src/headlesscheck.h @@ -21,14 +21,14 @@ #ifdef ENABLE_HEADLESS -#define TOMAHAWK_APPLICATION QtSingleCoreApplication +#define TOMAHAWK_APPLICATION QCoreApplication #define TOMAHAWK_HEADLESS -#include "qtsingleapp/qtsingleapplication.h" +#include > #else -#define TOMAHAWK_APPLICATION QtSingleApplication -#include "qtsingleapp/qtsingleapplication.h" +#define TOMAHAWK_APPLICATION QApplication +#include #include "tomahawkwindow.h" #endif diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index f45c391a3..e398e89a7 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -145,8 +145,10 @@ set( libSources widgets/overlaywidget.cpp widgets/infowidgets/sourceinfowidget.cpp - qtsingleapp/qtlocalpeer.cpp - qtsingleapp/qtsingleapplication.cpp + kdsingleapplicationguard/kdsingleapplicationguard.cpp + kdsingleapplicationguard/kdsharedmemorylocker.cpp + kdsingleapplicationguard/kdtoolsglobal.cpp + kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp ) set( libHeaders @@ -287,8 +289,10 @@ set( libHeaders widgets/overlaywidget.h widgets/infowidgets/sourceinfowidget.h - qtsingleapp/qtlocalpeer.h - qtsingleapp/qtsingleapplication.h + kdsingleapplicationguard/kdsingleapplicationguard.h + kdsingleapplicationguard/kdsharedmemorylocker.h + kdsingleapplicationguard/kdtoolsglobal.h + kdsingleapplicationguard/kdlockedsharedmemorypointer.h ) set( libHeaders_NoMOC diff --git a/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp new file mode 100644 index 000000000..e1fe10a4d --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp @@ -0,0 +1,475 @@ +#include "kdlockedsharedmemorypointer.h" + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) +#ifndef QT_NO_SHAREDMEMORY + +namespace kdtools +{ +} +using namespace kdtools; + +KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory * m ) + : locker( m ), + mem( m ) +{ + +} + +KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory & m ) + : locker( &m ), + mem( &m ) +{ + +} + +KDLockedSharedMemoryPointerBase::~KDLockedSharedMemoryPointerBase() {} + +void * KDLockedSharedMemoryPointerBase::get() { + return mem ? mem->data() : 0 ; +} + +const void * KDLockedSharedMemoryPointerBase::get() const { + return mem ? mem->data() : 0 ; +} + +size_t KDLockedSharedMemoryPointerBase::byteSize() const { + return mem->size(); +} + +/*! + \class KDLockedSharedMemoryPointer + \ingroup core raii smartptr + \brief Locking pointer for Qt shared memory segments + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + KDLockedSharedMemoryPointer is a smart immutable pointer, which gives convenient and safe access to a QSharedMemory data segment. + The content of a KDLockedSharedMemoryPointer cannot be changed during it's lifetime. + + You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it. + \note You can only put simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the + documentation of QSharedMemory for details. +*/ + +/*! + \fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory * mem ) + + Constructor. Constructs a KDLockedSharedMemory pointer which points to the data segment of \a mem. + The constructor locks \a mem. If the memory segment is already locked by another process, this constructor + blocks until the lock is released. + + \post data() == mem->data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory & mem ) + + \overload + + \post data() == mem.data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryPointer::~KDLockedSharedMemoryPointer() + + Destructor. Unlocks the shared memory segment. + + \post The shared memory segment has been unlocked +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::get() + + \returns a pointer to the contained object. +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::get() const + + \returns a const pointer to the contained object + \overload +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::data() + + Equivalent to get(), provided for consistency with Qt naming conventions. +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::data() const + + \overload +*/ + +/*! + \fn T & KDLockedSharedMemoryPointer::operator*() + + Dereference operator. Returns \link get() *get()\endlink. +*/ + +/*! + \fn const T & KDLockedSharedMemoryPointer::operator*() const + + Dereference operator. Returns \link get() *get()\endlink. + \overload +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::operator->() + + Member-by-pointer operator. Returns get(). +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::operator->() const + + Member-by-pointer operator. Returns get(). + \overload +*/ + +/*! + \class KDLockedSharedMemoryArray + \ingroup core raii smartptr + \brief Locking array pointer to Qt shared memory segments + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + KDLockedSharedMemoryArray is a smart immutable pointer, which gives convenient and safe access to array data stored in a QSharedMemory + data segment. + The content of a KDLockedSharedMemoryArray cannot be changed during it's lifetime. + + You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it. + \note You can only put arrays of simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the + documentation of QSharedMemory for details. + + \sa KDLockedSharedMemoryPointer +*/ + +/*! + \fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory* mem ) + Constructor. Constructs a KDLockedSharedMemoryArray which points to the data segment of \a mem. The constructor locks \a mem. If the memory + segment is already locked by another process, this constructor blocks until the lock is release. + + \post get() == mem->data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory& mem ) + \overload + + \post get() == mem->data() and the memory segment has been locked +*/ + + +/*! + \typedef KDLockedSharedMemoryArray::size_type + Typedef for std::size_t. Provided for STL compatibility. +*/ + +/*! + \typedef KDLockedSharedMemoryArray::difference_type + Typedef for std::ptrdiff_t. Provided for STL compatibility. +*/ + +/*! + \typedef KDLockedSharedMemoryArray::iterator + Typedef for T*. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::const_iterator + Typedef for const T*. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::reverse_iterator + Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::iterator iterator\endlink >. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::const_reverse_iterator + Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::const_iterator const_iterator\endlink >. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::begin() + Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the first item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::begin() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::end() + Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the item after the last item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::end() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rbegin() + Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the item after the last item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rbegin() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rend() + Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the first item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rend() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::size_type KDLockedSharedMemoryArray::size() const + Returns the size of this array. The size is calculated from the storage size of T and + the size of the shared memory segment. + \since_f 2.2 +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::operator[]( difference_type n ) + Array access operator. Returns a reference to the item at index position \a n. +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::operator[]( difference_type n ) const + \overload +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::front() + Returns a reference to the first item in the array. This is the same as operator[](0). +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::front() const + \overload +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::back() + Returns a reference to the last item in the array. This is the same as operator[](size()-1). + \since_f 2.2 +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::back() const + \overload + \since_f 2.2 +*/ + + +#ifdef eKDTOOLSCORE_UNITTESTS + +#include + +#include +#include + +namespace +{ + struct TestStruct + { + TestStruct( uint nn = 0 ) + : n( nn ), + f( 0.0 ), + c( '\0' ), + b( false ) + { + } + uint n; + double f; + char c; + bool b; + }; + + bool operator==( const TestStruct& lhs, const TestStruct& rhs ) + { + return lhs.n == rhs.n && lhs.f == rhs.f && lhs.c == rhs.c && lhs.b == rhs.b; + } + + class TestThread : public QThread + { + public: + TestThread( const QString& key ) + : mem( key ) + { + mem.attach(); + } + + void run() + { + while( true ) + { + msleep( 100 ); + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + if( !p->b ) + continue; + + p->n = 5; + p->f = 3.14; + p->c = 'A'; + p->b = false; + return; + } + } + + QSharedMemory mem; + }; + + bool isConst( TestStruct* ) + { + return false; + } + + bool isConst( const TestStruct* ) + { + return true; + } +} + + +KDAB_UNITTEST_SIMPLE( KDLockedSharedMemoryPointer, "kdcoretools" ) { + + const QString key = QUuid::createUuid(); + QSharedMemory mem( key ); + const bool created = mem.create( sizeof( TestStruct ) ); + assertTrue( created ); + if ( !created ) + return; // don't execute tests if shm coulnd't be created + + // On Windows, shared mem is only available in increments of page + // size (4k), so don't fail if the segment is larger: + const unsigned long mem_size = mem.size(); + assertGreaterOrEqual( mem_size, sizeof( TestStruct ) ); + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertTrue( p ); + *p = TestStruct(); + assertEqual( p->n, 0u ); + assertEqual( p->f, 0.0 ); + assertEqual( p->c, '\0' ); + assertFalse( p->b ); + } + + { + TestThread thread( key ); + assertEqual( thread.mem.key().toStdString(), key.toStdString() ); + assertEqual( static_cast< unsigned long >( thread.mem.size() ), mem_size ); + thread.start(); + + assertTrue( thread.isRunning() ); + thread.wait( 2000 ); + assertTrue( thread.isRunning() ); + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + p->b = true; + } + + thread.wait( 2000 ); + assertFalse( thread.isRunning() ); + } + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertEqual( p->n, 5u ); + assertEqual( p->f, 3.14 ); + assertEqual( p->c, 'A' ); + assertFalse( p->b ); + } + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( mem ); + assertEqual( mem.data(), p.get() ); + assertEqual( p.get(), p.operator->() ); + assertEqual( p.get(), &(*p) ); + assertEqual( p.get(), p.data() ); + assertFalse( isConst( p.get() ) ); + } + + { + const kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertEqual( mem.data(), p.get() ); + assertEqual( p.get(), p.operator->() ); + assertEqual( p.get(), &(*p) ); + assertEqual( p.get(), p.data() ); + assertTrue( isConst( p.get() ) ); + } + + { + QSharedMemory mem2( key + key ); + const bool created2 = mem2.create( 16 * sizeof( TestStruct ) ); + assertTrue( created2 ); + if ( !created2 ) + return; // don't execute tests if shm coulnd't be created + + kdtools::KDLockedSharedMemoryArray a( mem2 ); + assertTrue( a ); + assertEqual( a.get(), mem2.data() ); + assertEqual( &a[0], a.get() ); + + a[1] = a[0]; + assertTrue( a[0] == a[1] ); + + TestStruct ts; + ts.n = 5; + ts.f = 3.14; + a[0] = ts; + assertFalse( a[0] == a[1] ); + assertEqual( a.front().n, ts.n ); + assertEqual( a[0].f, ts.f ); + a[0].n = 10; + assertEqual( a.front().n, 10u ); + ts = a[0]; + assertEqual( ts.n, 10u ); + + std::vector< TestStruct > v; + for( uint i = 0; i < a.size(); ++i ) + v.push_back( TestStruct( i ) ); + + std::copy( v.begin(), v.end(), a.begin() ); + for( uint i = 0; i < a.size(); ++i ) + assertEqual( a[ i ].n, i ); + assertEqual( a.front().n, 0u ); + assertEqual( a.back().n, a.size() - 1 ); + + std::copy( v.begin(), v.end(), a.rbegin() ); + for( uint i = 0; i < a.size(); ++i ) + assertEqual( a[ i ].n, a.size() - 1 - i ); + assertEqual( a.front().n, a.size() - 1 ); + assertEqual( a.back().n, 0u ); + } + +} +#endif // KDTOOLSCORE_UNITTESTS +#endif // QT_NO_SHAREDMEMORY +#endif // QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) diff --git a/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h new file mode 100644 index 000000000..df0ea4998 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h @@ -0,0 +1,115 @@ +#ifndef __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ +#define __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ + +#include + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) +#ifndef QT_NO_SHAREDMEMORY + +#include "kdsharedmemorylocker.h" +#include + +#include + +#ifndef DOXYGEN_RUN +namespace kdtools { +#endif + +class KDLockedSharedMemoryPointerBase { +protected: + explicit KDLockedSharedMemoryPointerBase( QSharedMemory * mem ); + explicit KDLockedSharedMemoryPointerBase( QSharedMemory & mem ); + ~KDLockedSharedMemoryPointerBase(); + + // PENDING(marc) do we really want const propagation here? I + // usually declare all my RAII objects const... + void * get(); + const void * get() const; + + KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() ) + + size_t byteSize() const; + +private: + KDSharedMemoryLocker locker; + QSharedMemory * const mem; +}; + +template< typename T> +class MAKEINCLUDES_EXPORT KDLockedSharedMemoryPointer : KDLockedSharedMemoryPointerBase { + KDAB_DISABLE_COPY( KDLockedSharedMemoryPointer ); +public: + explicit KDLockedSharedMemoryPointer( QSharedMemory * m ) + : KDLockedSharedMemoryPointerBase( m ) {} + explicit KDLockedSharedMemoryPointer( QSharedMemory & m ) + : KDLockedSharedMemoryPointerBase( m ) {} + + T * get() { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + const T * get() const { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + + T * data() { return static_cast( get() ); } + const T * data() const { return static_cast( get() ); } + + T & operator*() { assert( get() ); return *get(); } + const T & operator*() const { assert( get() ); return *get(); } + + T * operator->() { return get(); } + const T * operator->() const { return get(); } + + KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase ) +}; + +template +class MAKEINCLUDES_EXPORT KDLockedSharedMemoryArray : KDLockedSharedMemoryPointerBase { + KDAB_DISABLE_COPY( KDLockedSharedMemoryArray ); +public: + explicit KDLockedSharedMemoryArray( QSharedMemory * m ) + : KDLockedSharedMemoryPointerBase( m ) {} + explicit KDLockedSharedMemoryArray( QSharedMemory & m ) + : KDLockedSharedMemoryPointerBase( m ) {} + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* iterator; + typedef const T* const_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + typedef std::reverse_iterator< iterator > reverse_iterator; + + iterator begin() { return get(); } + const_iterator begin() const { return get(); } + + iterator end() { return begin() + size(); } + const_iterator end() const { return begin() + size(); } + + reverse_iterator rbegin() { return reverse_iterator( end() ); } + const_reverse_iterator rbegin() const { return reverse_iterator( end() ); } + + reverse_iterator rend() { return reverse_iterator( begin() ); } + const_reverse_iterator rend() const { return const_reverse_iterator( begin() ); } + + size_type size() const { return byteSize() / sizeof( T ); } + + T * get() { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + const T * get() const { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + + T & operator[]( difference_type n ) { assert( get() ); return *(get()+n); } + const T & operator[]( difference_type n ) const { assert( get() ); return *(get()+n); } + + T & front() { assert( get() ); return *get(); } + const T & front() const { assert( get() ); return *get(); } + + T & back() { assert( get() ); return *( get() + size() - 1 ); } + const T & back() const { assert( get() ); return *( get() + size() - 1 ); } + + KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase ) +}; + +#ifndef DOXYGEN_RUN +} +#endif + +#endif /* QT_NO_SHAREDMEMORY */ + +#endif /* QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) */ + +#endif /* __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ */ diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp new file mode 100644 index 000000000..0c99b8fff --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp @@ -0,0 +1,40 @@ +#include "kdsharedmemorylocker.h" + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) + +#include + +using namespace kdtools; + +/*! + \class KDSharedMemoryLocker + \ingroup raii core + \brief Exception-safe and convenient wrapper around QSharedMemory::lock() +*/ + +/** + * Constructor. Locks the shared memory segment \a mem. + * If another process has locking the segment, this constructor blocks + * until the lock is released. The memory segments needs to be properly created or attached. + */ +KDSharedMemoryLocker::KDSharedMemoryLocker( QSharedMemory* mem ) + : mem( mem ) +{ + mem->lock(); +} + +/** + * Destructor. Unlocks the shared memory segment associated with this + * KDSharedMemoryLocker. + */ +KDSharedMemoryLocker::~KDSharedMemoryLocker() +{ + mem->unlock(); +} + +#ifdef KDAB_EVAL +#include KDAB_EVAL +static const EvalDialogChecker evalChecker( "KD Tools", false ); +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h new file mode 100644 index 000000000..7ae83e771 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h @@ -0,0 +1,36 @@ +#ifndef __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H +#define __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H + +#include "kdtoolsglobal.h" + +#if QT_VERSION < 0x040400 && !defined( DOXYGEN_RUN ) +#ifdef Q_CC_GNU +#warning "Can't use KDTools KDSharedMemoryLocker with Qt versions prior to 4.4" +#endif +#else + +class QSharedMemory; + +#ifndef DOXYGEN_RUN +namespace kdtools +{ +#endif + +class KDTOOLSCORE_EXPORT KDSharedMemoryLocker +{ + Q_DISABLE_COPY( KDSharedMemoryLocker ) +public: + KDSharedMemoryLocker( QSharedMemory* mem ); + ~KDSharedMemoryLocker(); + +private: + QSharedMemory* const mem; +}; + +#ifndef DOXYGEN_RUN +} +#endif + +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp new file mode 100644 index 000000000..63893dbde --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp @@ -0,0 +1,622 @@ +#include "kdsingleapplicationguard.h" + +#ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES +#define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 128 +#endif + +#ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE +#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 1024 +#endif + + + +KDSingleApplicationGuard::Instance::Instance( const QStringList& args, qint64 p ) + : arguments( args ), + pid( p ) +{ +} + +#if QT_VERSION < 0x040400 + +class KDSingleApplicationGuard::Private +{ +}; + +KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication*, Policy ) +{ + qWarning( "KD Tools was compiled with a Qt version prior to 4.4. SingleApplicationGuard won't work." ); +} + +KDSingleApplicationGuard::~KDSingleApplicationGuard() +{ +} + +void KDSingleApplicationGuard::shutdownOtherInstances() +{ +} + +void KDSingleApplicationGuard::killOtherInstances() +{ +} + +void KDSingleApplicationGuard::timerEvent( QTimerEvent* ) +{ +} +#else + +#include +#include + +#include "kdsharedmemorylocker.h" +#include "kdlockedsharedmemorypointer.h" + +#include +#include +#include + +#ifndef Q_WS_WIN +#include +#endif + +using namespace kdtools; + +/*! + \class KDSingleApplicationGuard KDSingleApplicationGuard + \brief A guard to protect an application from having several instances. + + KDSingleApplicationGuard can be used to make sure only one instance of an + application is running at the same time. + + \note As KDSingleApplicationGuard uses QSharedMemory Qt 4.4 or later is required + */ + +/*! + \fn void KDSingleApplicationGuard::instanceStarted() + This signal is emitted by the primary instance when ever one other + instance was started. + */ + +/*! + \fn void KDSingleApplicationGuard::instanceExited() + This signal is emitted by the primary instance when ever one other + instance was exited. + */ + +/*! + \fn void KDSingleApplicationGuard::becamePrimaryInstance() + This signal is emitted, when the current running application gets the new + primary application. The old primary application has quit. + */ + +enum Command +{ + NoCommand = 0x00, + ExitedInstance = 0x01, + NewInstance = 0x02, + FreeInstance = 0x04, + ShutDownCommand = 0x08, + KillCommand = 0x10, + BecomePrimaryCommand = 0x20 +}; + +Q_DECLARE_FLAGS( Commands, Command ) +Q_DECLARE_OPERATORS_FOR_FLAGS( Commands ) + +struct ProcessInfo +{ + explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 ) + : command( c ), + pid( p ) + { + std::fill_n( commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, '\0' ); + + int argpos = 0; + for( QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it ) + { + const QByteArray arg = it->toLatin1(); + const int count = qMin( KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos, arg.count() ); + std::copy( arg.begin(), arg.begin() + count, commandline + argpos ); + argpos += arg.count() + 1; // makes sure there's a \0 between every parameter + } + } + + QStringList arguments() const + { + QStringList result; + + QByteArray arg; + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE; ++i ) + { + if( commandline[ i ] == '\0' && !arg.isEmpty() ) + { + result.push_back( QString::fromLatin1( arg ) ); + arg.clear(); + } + else if( !commandline[ i ] == '\0' ) + { + arg.push_back( commandline[ i ] ); + } + } + + return result; + } + + Commands command; + char commandline[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ]; + qint64 pid; +}; + +bool operator==( const ProcessInfo& lhs, const ProcessInfo& rhs ) +{ + return lhs.command == rhs.command && + ::memcmp( lhs.commandline, rhs.commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ) == 0; +} + +bool operator!=( const ProcessInfo& lhs, const ProcessInfo& rhs ) +{ + return !operator==( lhs, rhs ); +} + +/*! + This struct contains information about the managed process system. + \internal + */ +struct InstanceRegister +{ + InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy ) + : policy( policy ) + { + std::fill_n( info, KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ); + ::memcpy( magicCookie, "kdsingleapp", 12 ); + } + + /*! + Returns wheter this register was properly initialized by the first instance. + */ + bool isValid() const + { + return ::strcmp( magicCookie, "kdsingleapp" ) == 0; + } + + ProcessInfo info[ KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ]; + KDSingleApplicationGuard::Policy policy; + char magicCookie[ 12 ]; +}; + +bool operator==( const InstanceRegister& lhs, const InstanceRegister& rhs ) +{ + if( lhs.policy != rhs.policy ) + return false; + + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + if( lhs.info[ i ] != rhs.info[ i ] ) + return false; + + return true; +} + +/*! + \internal + */ +class KDSingleApplicationGuard::Private +{ +public: + Private( KDSingleApplicationGuard* qq ) + : q( qq ), + id( -1 ) + { + if( primaryInstance == 0 ) + primaryInstance = q; + } + + ~Private() + { + if( primaryInstance == q ) + primaryInstance = 0; + } + + void shutdownInstance() + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem ); + instances->info[ q->d->id ].command = ExitedInstance; + + if( q->isPrimaryInstance() ) + { + // ohh... we need a new primary instance... + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 ) + { + instances->info[ i ].command |= BecomePrimaryCommand; + return; + } + } + // none found? then my species is dead :-( + } + } + + static KDSingleApplicationGuard* primaryInstance; + +private: + KDSingleApplicationGuard* const q; + +public: + Policy policy; + QSharedMemory mem; + int id; +}; + +KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0; + +#ifndef Q_WS_WIN +void SIGINT_handler( int sig ) +{ + if( sig == SIGINT && KDSingleApplicationGuard::Private::primaryInstance != 0 ) + KDSingleApplicationGuard::Private::primaryInstance->d->shutdownInstance(); + ::exit( 1 ); +} +#endif + +/*! + Creates a new KDSingleApplicationGuard guarding \a parent from mulitply instances. + If \a policy is AutoKillOtherInstances (the default), all instances, which try to start, + are killed automatically and instanceStarted() is emitted. + If \a policy is NoPolicy, the other instance will run and instanceStarted() is emitted. + */ +KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication* parent, Policy policy ) + : QObject( parent ), + d( new Private( this ) ) +{ + const QString name = parent->applicationName(); + Q_ASSERT_X( !name.isEmpty(), "KDSingleApplicationGuard::KDSingleApplicationGuard", "applicationName must not be emty" ); + d->mem.setKey( name ); + + // if another instance crashed, the shared memory segment is still there on Unix + // the following lines trigger deletion in that case +#ifndef Q_WS_WIN + d->mem.attach(); + d->mem.detach(); +#endif + + d->policy = policy; + + const bool created = d->mem.create( sizeof( InstanceRegister ) ); + if( !created ) + { + if( !d->mem.attach() ) + { + qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." ); + return; + } + + // lets wait till the other instance initialized the register + bool initialized = false; + while( !initialized ) + { + const KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + initialized = instances->isValid(); + } + } + + + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + if( !created ) + { + // we're _not_ the first instance + // but the + bool killOurSelf = false; + + // find a new slot... + d->id = std::find( instances->info, instances->info + KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ) - instances->info; + ProcessInfo& info = instances->info[ d->id ]; + info = ProcessInfo( NewInstance, parent->arguments(), QCoreApplication::applicationPid() ); + killOurSelf = instances->policy == AutoKillOtherInstances; + d->policy = instances->policy; + + // but the signal that we tried to start was sent to the primary application + if( killOurSelf ) + { + info.command |= ExitedInstance; + exit( 1 ); + } + } + else + { + // ok.... we are the first instance + InstanceRegister reg( policy ); // create a new list + d->id = 0; // our id = 0 + // and we've no command + reg.info[ 0 ] = ProcessInfo( NoCommand, parent->arguments(), QCoreApplication::applicationPid() ); + *instances = reg; // push this is the process list into shared memory + } + +#ifndef Q_WS_WIN + ::signal( SIGINT, SIGINT_handler ); +#endif + + // now listen for commands + startTimer( 250 ); +} + +/*! + Destroys this SingleApplicationGuard. + If this instance has been the primary instance and no other instance is existing anymore, + the application is shut down completely. Otherwise the destructor selects another instance to + be the primary instances. + */ +KDSingleApplicationGuard::~KDSingleApplicationGuard() +{ + if( d->id == -1 ) + return; + + d->shutdownInstance(); +} + +/*! + \property KDSingleApplicationGuard::primaryInstance + Determines wheter this instance is the primary instance. + The primary instance is the first instance which was started or an instance which + got selected by KDSingleApplicationGuard's destructor, when the primary instance was + shut down. + + Get this property's value using %isPrimaryInstance(), and monitor changes to it + using becamePrimaryInstance(). + */ +bool KDSingleApplicationGuard::isPrimaryInstance() const +{ + return d->id == 0; +} + +/*! + \property KDSingleApplicationGuard::Policy + Specifies the policy KDSingleApplicationGuard is using when new instances are started. + This can only be set in the primary instance. + + Get this property's value using %policy(), set it using %setPolicy(), and monitor changes + to it using policyChanged(). + */ +KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const +{ + return d->policy; +} + +void KDSingleApplicationGuard::setPolicy( Policy policy ) +{ + Q_ASSERT( isPrimaryInstance() ); + if( d->policy == policy ) + return; + + d->policy = policy; + emit policyChanged(); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + instances->policy = policy; +} + +/*! + Returns a list of all currently running instances. + */ +QList< KDSingleApplicationGuard::Instance > KDSingleApplicationGuard::instances() const +{ + QList< Instance > result; + const KDLockedSharedMemoryPointer< InstanceRegister > instances( const_cast< QSharedMemory* >( &d->mem ) ); + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + const ProcessInfo& info = instances->info[ i ]; + if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 ) + result.push_back( Instance( info.arguments(), info.pid ) ); + } + return result; +} + +/*! + Shuts down all other instances. This can only be called from the + the primary instance. + Shut down is done gracefully via QCoreApplication::quit(). + */ +void KDSingleApplicationGuard::shutdownOtherInstances() +{ + Q_ASSERT( isPrimaryInstance() ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = ShutDownCommand; + } +} + +/*! + Kills all other instances. This can only be called from the + the primary instance. + Killing is done via exit(1) + */ +void KDSingleApplicationGuard::killOtherInstances() +{ + Q_ASSERT( isPrimaryInstance() ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = KillCommand; + } +} + +/*! + \reimp + */ +void KDSingleApplicationGuard::timerEvent( QTimerEvent* event ) +{ + Q_UNUSED( event ) + + if( isPrimaryInstance() ) + { + // only the primary instance will get notified about new instances + QList< Instance > exitedInstances; + QList< Instance > startedInstances; + + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + ProcessInfo& info = instances->info[ i ]; + if( info.command & NewInstance ) + { + startedInstances.push_back( Instance( info.arguments(), info.pid ) ); + info.command &= ~NewInstance; // clear NewInstance flag + } + else if( info.command & ExitedInstance ) + { + exitedInstances.push_back( Instance( info.arguments(), info.pid ) ); + info.command = FreeInstance; // set FreeInstance flag + } + } + } + + // one signal for every new instance - _after_ the memory segment was unlocked again + for( QList< Instance >::const_iterator it = startedInstances.begin(); it != startedInstances.end(); ++it ) + emit instanceStarted( *it ); + for( QList< Instance >::const_iterator it = exitedInstances.begin(); it != exitedInstances.end(); ++it ) + emit instanceExited( *it ); + } + else + { + // do we have a command? + bool killOurSelf = false; + bool shutDownOurSelf = false; + bool policyDidChange = false; + + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + policyDidChange = instances->policy != d->policy; + d->policy = instances->policy; + + if( instances->info[ d->id ].command & BecomePrimaryCommand ) + { + // we became primary! + instances->info[ 0 ] = instances->info[ d->id ]; + instances->info[ d->id ] = ProcessInfo(); // change our id to 0 and declare the old slot as free + d->id = 0; + emit becamePrimaryInstance(); + } + + killOurSelf = instances->info[ d->id ].command & KillCommand; // check for kill command + shutDownOurSelf = instances->info[ d->id ].command & ShutDownCommand; // check for shut down command + instances->info[ d->id ].command &= ~( KillCommand | ShutDownCommand | BecomePrimaryCommand ); // reset both flags + if( killOurSelf ) + { + instances->info[ d->id ].command |= ExitedInstance; // upon kill, we have to set the ExitedInstance flag + d->id = -1; // becauso our d'tor won't be called anymore + } + } + + if( killOurSelf ) // kill our self takes precedence + exit( 1 ); + else if( shutDownOurSelf ) + qApp->quit(); + else if( policyDidChange ) + emit policyChanged(); + } +} + +#ifdef KDTOOLSCORE_UNITTESTS + +#include + +#include + +#include +#include +#include + +Q_DECLARE_METATYPE( KDSingleApplicationGuard::Instance ); + +static void wait( int msec ) +{ + QTime t; + t.start(); + while( t.elapsed() < msec ) + { + qApp->processEvents( QEventLoop::WaitForMoreEvents, msec - t.elapsed() ); + } +} + +static std::ostream& operator<<( std::ostream& stream, const QStringList& list ) +{ + stream << "QStringList("; + for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it ) + { + stream << " " << it->toLocal8Bit().data(); + if( it + 1 != list.end() ) + stream << ","; + } + stream << " )"; + return stream; +} + + +KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) { + + // set it to an unique name + qApp->setApplicationName( QUuid::createUuid().toString() ); + + qRegisterMetaType< KDSingleApplicationGuard::Instance >(); + + KDSingleApplicationGuard* guard3 = 0; + QSignalSpy* spy3 = 0; + + { + KDSingleApplicationGuard guard1( qApp ); + assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances ); + assertEqual( guard1.instances().count(), 1 ); + assertTrue( guard1.isPrimaryInstance() ); + + guard1.setPolicy( KDSingleApplicationGuard::NoPolicy ); + assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy ); + + QSignalSpy spy1( &guard1, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + + KDSingleApplicationGuard guard2( qApp ); + assertEqual( guard1.instances().count(), 2 ); + assertEqual( guard2.instances().count(), 2 ); + assertEqual( guard2.policy(), KDSingleApplicationGuard::NoPolicy ); + assertFalse( guard2.isPrimaryInstance() ); + + wait( 1000 ); + + assertEqual( spy1.count(), 1 ); + guard3 = new KDSingleApplicationGuard( qApp ); + spy3 = new QSignalSpy( guard3, SIGNAL( becamePrimaryInstance() ) ); + assertFalse( guard3->isPrimaryInstance() ); + } + + wait( 1000 ); + assertEqual( spy3->count(), 1 ); + assertEqual( guard3->instances().count(), 1 ); + assertTrue( guard3->isPrimaryInstance() ); + + assertEqual( guard3->instances().first().arguments, qApp->arguments() ); + + QSignalSpy spyStarted( guard3, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + QSignalSpy spyExited( guard3, SIGNAL( instanceExited( KDSingleApplicationGuard::Instance ) ) ); + + { + KDSingleApplicationGuard guard1( qApp ); + KDSingleApplicationGuard guard2( qApp ); + + wait( 1000 ); + + assertEqual( spyStarted.count(), 2 ); + } + + wait( 1000 ); + assertEqual( spyExited.count(), 2 ); + + delete spy3; + delete guard3; + } + +#endif // KDTOOLSCORE_UNITTESTS + +#endif // QT_VERSION < 0x040400 diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h new file mode 100644 index 000000000..f649ef836 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h @@ -0,0 +1,74 @@ +#ifndef __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ +#define __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ + +#include +#include + +#include "pimpl_ptr.h" + +class QCoreApplication; + +#ifndef Q_WS_WIN +void SIGINT_handler( int sig ); +#endif + +class KDTOOLSCORE_EXPORT KDSingleApplicationGuard : public QObject +{ + Q_OBJECT +#ifndef Q_WS_WIN + friend void ::SIGINT_handler( int ); +#endif + +public: + enum Policy + { + NoPolicy = 0, + AutoKillOtherInstances = 1 + }; + + Q_PROPERTY( bool primaryInstance READ isPrimaryInstance NOTIFY becamePrimaryInstance ) + Q_PROPERTY( Policy policy READ policy WRITE setPolicy NOTIFY policyChanged ) + + explicit KDSingleApplicationGuard( QCoreApplication* parent, Policy policy = AutoKillOtherInstances ); + ~KDSingleApplicationGuard(); + + bool isPrimaryInstance() const; + + Policy policy() const; + void setPolicy( Policy policy ); + + struct Instance + { + Instance( const QStringList& arguments = QStringList(), qint64 pid = -1 ); + + QStringList arguments; + qint64 pid; + }; + + QList< Instance > instances() const; + +Q_SIGNALS: + void instanceStarted( KDSingleApplicationGuard::Instance instance ); + void instanceExited( KDSingleApplicationGuard::Instance instance ); + void becamePrimaryInstance(); + void policyChanged(); + +public Q_SLOTS: + void shutdownOtherInstances(); + void killOtherInstances(); + +protected: + void timerEvent( QTimerEvent* event ); + +private: + class Private; + kdtools::pimpl_ptr< Private > d; +}; + +#if QT_VERSION < 0x040400 +#ifdef Q_CC_GNU +#warning "Can't use KDSingleApplicationGuard with Qt versions prior to 4.4" +#endif +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp new file mode 100644 index 000000000..5997fe64c --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp @@ -0,0 +1,32 @@ +#include "kdtoolsglobal.h" + +#include + +#include + +namespace { + struct Version { + unsigned char v[3]; + }; + + static inline bool operator<( const Version & lhs, const Version & rhs ) { + return std::lexicographical_compare( lhs.v, lhs.v + 3, rhs.v, rhs.v + 3 ); + } + static inline bool operator==( const Version & lhs, const Version & rhs ) { + return std::equal( lhs.v, lhs.v + 3, rhs.v ); + } + KDTOOLS_MAKE_RELATION_OPERATORS( Version, static inline ) +} + +static Version kdParseQtVersion( const char * const version ) { + if ( !version || qstrlen( version ) < 5 || version[1] != '.' || version[3] != '.' || version[5] != 0 && version[5] != '.' && version[5] != '-' ) + return Version(); // parse error + const Version result = { { version[0] - '0', version[2] - '0', version[4] - '0' } }; + return result; +} + +bool _kdCheckQtVersion_impl( int major, int minor, int patchlevel ) { + static const Version actual = kdParseQtVersion( qVersion() ); // do this only once each run... + const Version requested = { { major, minor, patchlevel } }; + return actual >= requested; +} diff --git a/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h new file mode 100644 index 000000000..4e8d0d673 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h @@ -0,0 +1,113 @@ +#ifndef __KDTOOLS_KDTOOLSGLOBAL_H__ +#define __KDTOOLS_KDTOOLSGLOBAL_H__ + +#include + +#define KDAB_DISABLE_COPY( x ) private: x( const x & ); x & operator=( const x & ) + +#ifdef KDTOOLS_SHARED +# ifdef BUILD_SHARED_KDTOOLSCORE +# define KDTOOLSCORE_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSCORE_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDTOOLSGUI +# define KDTOOLSGUI_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSGUI_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDTOOLSXML +# define KDTOOLSXML_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSXML_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDUPDATER +# define KDTOOLS_UPDATER_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLS_UPDATER_EXPORT Q_DECL_IMPORT +# endif +#else // KDTOOLS_SHARED +# define KDTOOLSCORE_EXPORT +# define KDTOOLSGUI_EXPORT +# define KDTOOLSXML_EXPORT +# define KDTOOLS_UPDATER_EXPORT +#endif // KDTOOLS_SHARED + +#define MAKEINCLUDES_EXPORT + +#define DOXYGEN_PROPERTY( x ) +#ifdef DOXYGEN_RUN +# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) operator unspecified_bool_type() const { return func; } +# define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) operator unspecified_bool_type() const; +#else +# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) \ + private: struct __safe_bool_dummy__ { void nonnull() {} }; \ + typedef void ( __safe_bool_dummy__::*unspecified_bool_type )(); \ + public: \ + operator unspecified_bool_type() const { \ + return ( func ) ? &__safe_bool_dummy__::nonnull : 0 ; \ + } +#define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) \ + using Class::operator Class::unspecified_bool_type; +#endif + +#define KDTOOLS_MAKE_RELATION_OPERATORS( Class, linkage ) \ + linkage bool operator>( const Class & lhs, const Class & rhs ) { \ + return operator<( rhs, lhs ); \ + } \ + linkage bool operator!=( const Class & lhs, const Class & rhs ) { \ + return !operator==( lhs, rhs ); \ + } \ + linkage bool operator<=( const Class & lhs, const Class & rhs ) { \ + return !operator>( lhs, rhs ); \ + } \ + linkage bool operator>=( const Class & lhs, const Class & rhs ) { \ + return !operator<( lhs, rhs ); \ + } + +template +inline T & __kdtools__dereference_for_methodcall( T & o ) { + return o; +} + +template +inline T & __kdtools__dereference_for_methodcall( T * o ) { + return *o; +} + +#define KDAB_SET_OBJECT_NAME( x ) __kdtools__dereference_for_methodcall( x ).setObjectName( QLatin1String( #x ) ) + +KDTOOLSCORE_EXPORT bool _kdCheckQtVersion_impl( int major, int minor=0, int patchlevel=0 ); +static inline bool kdCheckQtVersion( unsigned int major, unsigned int minor=0, unsigned int patchlevel=0 ) { + return (major<<16|minor<<8|patchlevel) <= static_cast(QT_VERSION) + || _kdCheckQtVersion_impl( major, minor, patchlevel ); +} + +#define KDTOOLS_DECLARE_PRIVATE_BASE( Class ) \ +protected: \ + class Private; \ + Private * d_func() { return _d; } \ + const Private * d_func() const { return _d; } \ + Class( Private * _d_, bool b ) : _d( _d_ ) { init(b); } \ +private: \ + void init(bool); \ +private: \ + Private * _d + +#define KDTOOLS_DECLARE_PRIVATE_DERIVED( Class, Base ) \ +protected: \ + class Private; \ + Private * d_func() { \ + return reinterpret_cast( Base::d_func() ); \ + } \ + const Private * d_func() const { \ + return reinterpret_cast( Base::d_func() ); \ + } \ + Class( Private * _d_, bool b ) \ + : Base( reinterpret_cast(_d_), b ) { init(b); } \ +private: \ + void init(bool) + + +#endif /* __KDTOOLS_KDTOOLSGLOBAL_H__ */ + diff --git a/src/libtomahawk/kdsingleapplicationguard/license-gpl b/src/libtomahawk/kdsingleapplicationguard/license-gpl new file mode 100644 index 000000000..332ed973c --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/license-gpl @@ -0,0 +1,349 @@ + + The KD Tools Library is Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. + + You may use, distribute and copy the KD Tools Library under the terms of + GNU General Public License version 2, which is displayed below. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +------------------------------------------------------------------------- diff --git a/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp new file mode 100644 index 000000000..3045ebc2a --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp @@ -0,0 +1,203 @@ +#include "pimpl_ptr.h" + +/*! + \class pimpl_ptr: + \ingroup core smartptr + \brief Owning pointer for private implementations + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + pimpl_ptr is a smart immutable pointer, which owns the contained object. Unlike other smart pointers, + it creates a standard constructed object when instanciated via the + \link pimpl_ptr() standard constructor\endlink. + Additionally, pimpl_ptr respects constness of the pointer object and returns \c const \c T* for + a const pimpl_ptr object. + + The content of a pimpl_ptr cannot be changed during it's lifetime. + + \section general-use General Use + + The general use case of pimpl_ptr is the "Pimpl Idiom", i.e. hiding the private implementation of a class + from the user's compiler which see \c MyClass as + + \code + class MyClass + { + public: + MyClass(); + ~MyClass(); + + // public class API + int value() const; + + private: + class Private; // defined later + kdtools::pimpl_ptr< Private > d; + }; + \endcode + + but not the private parts of it. These can only be seen (and accessed) by the code knowing \c MyClass::Private: + + \code + class MyClass::Private + { + public: + int value; + }; + + MyClass::MyClass() + { + // d was automatically filled with new Private + d->value = 42; + } + + MyClass::~MyClass() + { + // the content of d gets deleted automatically + } + + int MyClass::value() const + { + // access the private part: + // since MyClass::value() is const, the returned pointee is const, too + return d->value; + } + \endcode + +*/ + +/*! + \fn pimpl_ptr::pimpl_ptr() + + Default constructor. Constructs a pimpl_tr that contains (owns) a standard constructed + instance of \c T. + + \post \c *this owns a new object. +*/ + +/*! + \fn pimpl_ptr::pimpl_ptr( T * t ) + + Constructor. Constructs a pimpl_ptr that contains (owns) \a t. + + \post get() == obj +*/ + +/*! + \fn pimpl_ptr::~pimpl_ptr() + + Destructor. + + \post The object previously owned by \c *this has been deleted. +*/ + +/*! + \fn const T * pimpl_ptr::get() const + + \returns a const pointer to the contained (owned) object. + \overload +*/ + +/*! + \fn T * pimpl_ptr::get() + + \returns a pointer to the contained (owned) object. +*/ + +/*! + \fn const T & pimpl_ptr::operator*() const + + Dereference operator. Returns \link get() *get()\endlink. + \overload +*/ + +/*! + \fn T & pimpl_ptr::operator*() + + Dereference operator. Returns \link get() *get()\endlink. +*/ + +/*! + \fn const T * pimpl_ptr::operator->() const + + Member-by-pointer operator. Returns get(). + \overload +*/ + +/*! + \fn T * pimpl_ptr::operator->() + + Member-by-pointer operator. Returns get(). +*/ + +#ifdef KDTOOLSCORE_UNITTESTS + +#include + +#include +#include + +namespace +{ + struct ConstTester + { + bool isConst() + { + return false; + } + + bool isConst() const + { + return true; + } + }; +} + +KDAB_UNITTEST_SIMPLE( pimpl_ptr, "kdcoretools" ) { + + { + kdtools::pimpl_ptr< QObject > p; + assertNotNull( p.get() ); + assertNull( p->parent() ); + } + + + { + QPointer< QObject > o; + { + kdtools::pimpl_ptr< QObject > qobject( new QObject ); + o = qobject.get(); + assertEqual( o, qobject.operator->() ); + assertEqual( o, &(qobject.operator*()) ); + } + assertNull( o ); + } + + { + const kdtools::pimpl_ptr< QObject > qobject( new QObject ); + const QObject* o = qobject.get(); + assertEqual( o, qobject.operator->() ); + assertEqual( o, &(qobject.operator*()) ); + } + + { + kdtools::pimpl_ptr< QObject > o1; + assertTrue( o1 ); + kdtools::pimpl_ptr< QObject > o2( 0 ); + assertFalse( o2 ); + } + + { + const kdtools::pimpl_ptr< ConstTester > o1; + kdtools::pimpl_ptr< ConstTester > o2; + assertTrue( o1->isConst() ); + assertFalse( o2->isConst() ); + assertTrue( (*o1).isConst() ); + assertFalse( (*o2).isConst() ); + assertTrue( o1.get()->isConst() ); + assertFalse( o2.get()->isConst() ); + } +} + +#endif // KDTOOLSCORE_UNITTESTS diff --git a/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h new file mode 100644 index 000000000..7b7f36839 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h @@ -0,0 +1,44 @@ +#ifndef __KDTOOLSCORE__PIMPL_PTR_H__ +#define __KDTOOLSCORE__PIMPL_PTR_H__ + +#include "kdtoolsglobal.h" + +#ifndef DOXYGEN_RUN +namespace kdtools { +#endif + + template + class pimpl_ptr { + KDAB_DISABLE_COPY( pimpl_ptr ); + T * d; + public: + pimpl_ptr() : d( new T ) {} + explicit pimpl_ptr( T * t ) : d( t ) {} + ~pimpl_ptr() { delete d; d = 0; } + + T * get() { return d; } + const T * get() const { return d; } + + T * operator->() { return get(); } + const T * operator->() const { return get(); } + + T & operator*() { return *get(); } + const T & operator*() const { return *get(); } + + KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() ) + }; + + // these are not implemented, so's we can catch their use at + // link-time. Leaving them undeclared would open up a comparison + // via operator unspecified-bool-type(). + template + void operator==( const pimpl_ptr &, const pimpl_ptr & ); + template + void operator!=( const pimpl_ptr &, const pimpl_ptr & ); + +#ifndef DOXYGEN_RUN +} // namespace kdtools +#endif + +#endif /* __KDTOOLSCORE__PIMPL_PTR_H__ */ + diff --git a/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp b/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp deleted file mode 100644 index 382d182dc..000000000 --- a/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtlocalpeer.h" -#include -#include - -#if defined(Q_OS_WIN) -#include -#include -typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); -static PProcessIdToSessionId pProcessIdToSessionId = 0; -#endif -#if defined(Q_OS_UNIX) -#include -#endif - -namespace QtLP_Private { -#include "qtlockedfile.cpp" -#if defined(Q_OS_WIN) -#include "qtlockedfile_win.cpp" -#else -#include "qtlockedfile_unix.cpp" -#endif -} - -const char* QtLocalPeer::ack = "ack"; - -QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) - : QObject(parent), id(appId) -{ - QString prefix = id; - if (id.isEmpty()) { - id = QCoreApplication::applicationFilePath(); -#if defined(Q_OS_WIN) - id = id.toLower(); -#endif - prefix = id.section(QLatin1Char('/'), -1); - } - prefix.remove(QRegExp("[^a-zA-Z]")); - prefix.truncate(6); - - QByteArray idc = id.toUtf8(); - quint16 idNum = qChecksum(idc.constData(), idc.size()); - socketName = QLatin1String("qtsingleapp-") + prefix - + QLatin1Char('-') + QString::number(idNum, 16); - -#if defined(Q_OS_WIN) - if (!pProcessIdToSessionId) { - QLibrary lib("kernel32"); - pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); - } - if (pProcessIdToSessionId) { - DWORD sessionId = 0; - pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); - socketName += QLatin1Char('-') + QString::number(sessionId, 16); - } -#else - socketName += QLatin1Char('-') + QString::number(::getuid(), 16); -#endif - - server = new QLocalServer(this); - QString lockName = QDir(QDir::tempPath()).absolutePath() - + QLatin1Char('/') + socketName - + QLatin1String("-lockfile"); - lockFile.setFileName(lockName); - lockFile.open(QIODevice::ReadWrite); -} - - - -bool QtLocalPeer::isClient() -{ - if (lockFile.isLocked()) - return false; - - if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) - return true; - - bool res = server->listen(socketName); -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) - // ### Workaround - if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { - QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); - res = server->listen(socketName); - } -#endif - if (!res) - qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); - QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); - return false; -} - - -bool QtLocalPeer::sendMessage(const QString &message, int timeout) -{ - if (!isClient()) - return false; - - QLocalSocket socket; - bool connOk = false; - for(int i = 0; i < 2; i++) { - // Try twice, in case the other instance is just starting up - socket.connectToServer(socketName); - connOk = socket.waitForConnected(timeout/2); - if (connOk || i) - break; - int ms = 250; -#if defined(Q_OS_WIN) - Sleep(DWORD(ms)); -#else - struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; - nanosleep(&ts, NULL); -#endif - } - if (!connOk) - return false; - - QByteArray uMsg(message.toUtf8()); - QDataStream ds(&socket); - ds.writeBytes(uMsg.constData(), uMsg.size()); - bool res = socket.waitForBytesWritten(timeout); - if (res) { - res &= socket.waitForReadyRead(timeout); // wait for ack - if (res) - res &= (socket.read(qstrlen(ack)) == ack); - } - return res; -} - - -void QtLocalPeer::receiveConnection() -{ - QLocalSocket* socket = server->nextPendingConnection(); - if (!socket) - return; - - while (socket->bytesAvailable() < (int)sizeof(quint32)) - socket->waitForReadyRead(); - QDataStream ds(socket); - QByteArray uMsg; - quint32 remaining; - ds >> remaining; - uMsg.resize(remaining); - int got = 0; - char* uMsgBuf = uMsg.data(); - do { - got = ds.readRawData(uMsgBuf, remaining); - remaining -= got; - uMsgBuf += got; - } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); - if (got < 0) { - qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); - delete socket; - return; - } - QString message(QString::fromUtf8(uMsg)); - socket->write(ack, qstrlen(ack)); - socket->waitForBytesWritten(1000); - delete socket; - emit messageReceived(message); //### (might take a long time to return) -} diff --git a/src/libtomahawk/qtsingleapp/qtlocalpeer.h b/src/libtomahawk/qtsingleapp/qtlocalpeer.h deleted file mode 100644 index 869af2ac2..000000000 --- a/src/libtomahawk/qtsingleapp/qtlocalpeer.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include -#include -#include - -#include "qtlockedfile.h" - -class QtLocalPeer : public QObject -{ - Q_OBJECT - -public: - QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); - bool isClient(); - bool sendMessage(const QString &message, int timeout); - QString applicationId() const - { return id; } - -Q_SIGNALS: - void messageReceived(const QString &message); - -protected Q_SLOTS: - void receiveConnection(); - -protected: - QString id; - QString socketName; - QLocalServer* server; - QtLP_Private::QtLockedFile lockFile; - -private: - static const char* ack; -}; diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile.cpp deleted file mode 100644 index 3e73ba652..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include "qtlockedfile.h" - -/*! - \class QtLockedFile - - \brief The QtLockedFile class extends QFile with advisory locking - functions. - - A file may be locked in read or write mode. Multiple instances of - \e QtLockedFile, created in multiple processes running on the same - machine, may have a file locked in read mode. Exactly one instance - may have it locked in write mode. A read and a write lock cannot - exist simultaneously on the same file. - - The file locks are advisory. This means that nothing prevents - another process from manipulating a locked file using QFile or - file system functions offered by the OS. Serialization is only - guaranteed if all processes that access the file use - QLockedFile. Also, while holding a lock on a file, a process - must not open the same file again (through any API), or locks - can be unexpectedly lost. - - The lock provided by an instance of \e QtLockedFile is released - whenever the program terminates. This is true even when the - program crashes and no destructors are called. -*/ - -/*! \enum QtLockedFile::LockMode - - This enum describes the available lock modes. - - \value ReadLock A read lock. - \value WriteLock A write lock. - \value NoLock Neither a read lock nor a write lock. -*/ - -/*! - Constructs an unlocked \e QtLockedFile object. This constructor - behaves in the same way as \e QFile::QFile(). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile() - : QFile() -{ -#ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Constructs an unlocked QtLockedFile object with file \a name. This - constructor behaves in the same way as \e QFile::QFile(const - QString&). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile(const QString &name) - : QFile(name) -{ -#ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Opens the file in OpenMode \a mode. - - This is identical to QFile::open(), with the one exception that the - Truncate mode flag is disallowed. Truncation would conflict with the - advisory file locking, since the file would be modified before the - write lock is obtained. If truncation is required, use resize(0) - after obtaining the write lock. - - Returns true if successful; otherwise false. - - \sa QFile::open(), QFile::resize() -*/ -bool QtLockedFile::open(OpenMode mode) -{ - if (mode & QIODevice::Truncate) { - qWarning("QtLockedFile::open(): Truncate mode not allowed."); - return false; - } - return QFile::open(mode); -} - -/*! - Returns \e true if this object has a in read or write lock; - otherwise returns \e false. - - \sa lockMode() -*/ -bool QtLockedFile::isLocked() const -{ - return m_lock_mode != NoLock; -} - -/*! - Returns the type of lock currently held by this object, or \e - QtLockedFile::NoLock. - - \sa isLocked() -*/ -QtLockedFile::LockMode QtLockedFile::lockMode() const -{ - return m_lock_mode; -} - -/*! - \fn bool QtLockedFile::lock(LockMode mode, bool block = true) - - Obtains a lock of type \a mode. The file must be opened before it - can be locked. - - If \a block is true, this function will block until the lock is - aquired. If \a block is false, this function returns \e false - immediately if the lock cannot be aquired. - - If this object already has a lock of type \a mode, this function - returns \e true immediately. If this object has a lock of a - different type than \a mode, the lock is first released and then a - new lock is obtained. - - This function returns \e true if, after it executes, the file is - locked by this object, and \e false otherwise. - - \sa unlock(), isLocked(), lockMode() -*/ - -/*! - \fn bool QtLockedFile::unlock() - - Releases a lock. - - If the object has no lock, this function returns immediately. - - This function returns \e true if, after it executes, the file is - not locked by this object, and \e false otherwise. - - \sa lock(), isLocked(), lockMode() -*/ - -/*! - \fn QtLockedFile::~QtLockedFile() - - Destroys the \e QtLockedFile object. If any locks were held, they - are released. -*/ diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile.h b/src/libtomahawk/qtsingleapp/qtlockedfile.h deleted file mode 100644 index 07a42bffb..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#ifndef QTLOCKEDFILE_H -#define QTLOCKEDFILE_H - -#include -#ifdef Q_OS_WIN -#include -#endif - -#if defined(Q_WS_WIN) -# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) -# define QT_QTLOCKEDFILE_EXPORT -# elif defined(QT_QTLOCKEDFILE_IMPORT) -# if defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# endif -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) -# elif defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) -# endif -#else -# define QT_QTLOCKEDFILE_EXPORT -#endif - -namespace QtLP_Private { - -class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile -{ -public: - enum LockMode { NoLock = 0, ReadLock, WriteLock }; - - QtLockedFile(); - QtLockedFile(const QString &name); - ~QtLockedFile(); - - bool open(OpenMode mode); - - bool lock(LockMode mode, bool block = true); - bool unlock(); - bool isLocked() const; - LockMode lockMode() const; - -private: -#ifdef Q_OS_WIN - Qt::HANDLE wmutex; - Qt::HANDLE rmutex; - QVector rmutexes; - QString mutexname; - - Qt::HANDLE getMutexHandle(int idx, bool doCreate); - bool waitMutex(Qt::HANDLE mutex, bool doBlock); - -#endif - LockMode m_lock_mode; -}; -} -#endif diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp deleted file mode 100644 index 715c7d9b1..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include -#include -#include -#include - -#include "qtlockedfile.h" - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; - int cmd = block ? F_SETLKW : F_SETLK; - int ret = fcntl(handle(), cmd, &fl); - - if (ret == -1) { - if (errno != EINTR && errno != EAGAIN) - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - - m_lock_mode = mode; - return true; -} - - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = F_UNLCK; - int ret = fcntl(handle(), F_SETLKW, &fl); - - if (ret == -1) { - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - m_lock_mode = NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); -} - diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp deleted file mode 100644 index 8090470cd..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include "qtlockedfile.h" -#include -#include - -#define MUTEX_PREFIX "QtLockedFile mutex " -// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS -#define MAX_READERS MAXIMUM_WAIT_OBJECTS - -#define TCHAR WCHAR - -Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) -{ - if (mutexname.isEmpty()) { - QFileInfo fi(*this); - mutexname = QString::fromLatin1(MUTEX_PREFIX) - + fi.absoluteFilePath().toLower(); - } - QString mname(mutexname); - if (idx >= 0) - mname += QString::number(idx); - - Qt::HANDLE mutex; - if (doCreate) { - QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); - return 0; - } - } - else { - QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) - qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); - return 0; - } - } - return mutex; -} - -bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) -{ - Q_ASSERT(mutex); - DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); - switch (res) { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: - return true; - break; - case WAIT_TIMEOUT: - break; - default: - qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); - } - return false; -} - - - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - if (!wmutex && !(wmutex = getMutexHandle(-1, true))) - return false; - - if (!waitMutex(wmutex, block)) - return false; - - if (mode == ReadLock) { - int idx = 0; - for (; idx < MAX_READERS; idx++) { - rmutex = getMutexHandle(idx, false); - if (!rmutex || waitMutex(rmutex, false)) - break; - CloseHandle(rmutex); - } - bool ok = true; - if (idx >= MAX_READERS) { - qWarning("QtLockedFile::lock(): too many readers"); - rmutex = 0; - ok = false; - } - else if (!rmutex) { - rmutex = getMutexHandle(idx, true); - if (!rmutex || !waitMutex(rmutex, false)) - ok = false; - } - if (!ok && rmutex) { - CloseHandle(rmutex); - rmutex = 0; - } - ReleaseMutex(wmutex); - if (!ok) - return false; - } - else { - Q_ASSERT(rmutexes.isEmpty()); - for (int i = 0; i < MAX_READERS; i++) { - Qt::HANDLE mutex = getMutexHandle(i, false); - if (mutex) - rmutexes.append(mutex); - } - if (rmutexes.size()) { - DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), - TRUE, block ? INFINITE : 0); - if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { - if (res != WAIT_TIMEOUT) - qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); - m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky - unlock(); - return false; - } - } - } - - m_lock_mode = mode; - return true; -} - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - if (m_lock_mode == ReadLock) { - ReleaseMutex(rmutex); - CloseHandle(rmutex); - rmutex = 0; - } - else { - foreach(Qt::HANDLE mutex, rmutexes) { - ReleaseMutex(mutex); - CloseHandle(mutex); - } - rmutexes.clear(); - ReleaseMutex(wmutex); - } - - m_lock_mode = QtLockedFile::NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); - if (wmutex) - CloseHandle(wmutex); -} diff --git a/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp b/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp deleted file mode 100644 index 5a8f1b035..000000000 --- a/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtsingleapplication.h" -#include "qtlocalpeer.h" -#include - - -/*! - \class QtSingleApplication qtsingleapplication.h - \brief The QtSingleApplication class provides an API to detect and - communicate with running instances of an application. - - This class allows you to create applications where only one - instance should be running at a time. I.e., if the user tries to - launch another instance, the already running instance will be - activated instead. Another usecase is a client-server system, - where the first started instance will assume the role of server, - and the later instances will act as clients of that server. - - By default, the full path of the executable file is used to - determine whether two processes are instances of the same - application. You can also provide an explicit identifier string - that will be compared instead. - - The application should create the QtSingleApplication object early - in the startup phase, and call isRunning() to find out if another - instance of this application is already running. If isRunning() - returns false, it means that no other instance is running, and - this instance has assumed the role as the running instance. In - this case, the application should continue with the initialization - of the application user interface before entering the event loop - with exec(), as normal. - - The messageReceived() signal will be emitted when the running - application receives messages from another instance of the same - application. When a message is received it might be helpful to the - user to raise the application so that it becomes visible. To - facilitate this, QtSingleApplication provides the - setActivationWindow() function and the activateWindow() slot. - - If isRunning() returns true, another instance is already - running. It may be alerted to the fact that another instance has - started by using the sendMessage() function. Also data such as - startup parameters (e.g. the name of the file the user wanted this - new instance to open) can be passed to the running instance with - this function. Then, the application should terminate (or enter - client mode). - - If isRunning() returns true, but sendMessage() fails, that is an - indication that the running instance is frozen. - - Here's an example that shows how to convert an existing - application to use QtSingleApplication. It is very simple and does - not make use of all QtSingleApplication's functionality (see the - examples for that). - - \code - // Original - int main(int argc, char **argv) - { - QApplication app(argc, argv); - - MyMainWidget mmw; - mmw.show(); - return app.exec(); - } - - // Single instance - int main(int argc, char **argv) - { - QtSingleApplication app(argc, argv); - - if (app.isRunning()) - return !app.sendMessage(someDataString); - - MyMainWidget mmw; - app.setActivationWindow(&mmw); - mmw.show(); - return app.exec(); - } - \endcode - - Once this QtSingleApplication instance is destroyed (normally when - the process exits or crashes), when the user next attempts to run the - application this instance will not, of course, be encountered. The - next instance to call isRunning() or sendMessage() will assume the - role as the new running instance. - - For console (non-GUI) applications, QtSingleCoreApplication may be - used instead of this class, to avoid the dependency on the QtGui - library. - - \sa QtSingleCoreApplication -*/ - - -void QtSingleApplication::sysInit(const QString &appId) -{ - actWin = 0; - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a GUIenabled are passed on to the QAppliation constructor. - - If you are creating a console application (i.e. setting \a - GUIenabled to false), you may consider using - QtSingleCoreApplication instead. -*/ - -QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) - : QApplication(argc, argv, GUIenabled) -{ - sysInit(); -} - - -/*! - Creates a QtSingleApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QAppliation constructor. -*/ - -QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) - : QApplication(argc, argv) -{ - sysInit(appId); -} - - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a type are passed on to the QAppliation constructor. -*/ -QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) - : QApplication(argc, argv, type) -{ - sysInit(); -} - - -#if defined(Q_WS_X11) -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, - and \a cmap are passed on to the QApplication constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be \a appId. \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(appId); -} -#endif - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ -bool QtSingleApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ -QString QtSingleApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - Sets the activation window of this application to \a aw. The - activation window is the widget that will be activated by - activateWindow(). This is typically the application's main window. - - If \a activateOnMessage is true (the default), the window will be - activated automatically every time a message is received, just prior - to the messageReceived() signal being emitted. - - \sa activateWindow(), messageReceived() -*/ - -void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) -{ - actWin = aw; - if (activateOnMessage) - connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); - else - disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); -} - - -/*! - Returns the applications activation window if one has been set by - calling setActivationWindow(), otherwise returns 0. - - \sa setActivationWindow() -*/ -QWidget* QtSingleApplication::activationWindow() const -{ - return actWin; -} - - -/*! - De-minimizes, raises, and activates this application's activation window. - This function does nothing if no activation window has been set. - - This is a convenience function to show the user that this - application instance has been activated when he has tried to start - another instance. - - This function should typically be called in response to the - messageReceived() signal. By default, that will happen - automatically, if an activation window has been set. - - \sa setActivationWindow(), messageReceived(), initialize() -*/ -void QtSingleApplication::activateWindow() -{ - if (actWin) { - actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); - actWin->raise(); - actWin->activateWindow(); - } -} - - -/*! - \fn void QtSingleApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage(), setActivationWindow(), activateWindow() -*/ - - -/*! - \fn void QtSingleApplication::initialize(bool dummy = true) - - \obsolete -*/ diff --git a/src/libtomahawk/qtsingleapp/qtsingleapplication.h b/src/libtomahawk/qtsingleapp/qtsingleapplication.h deleted file mode 100644 index c696d60ce..000000000 --- a/src/libtomahawk/qtsingleapp/qtsingleapplication.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include - -#include "dllmacro.h" - -class QtLocalPeer; - -class DLLEXPORT QtSingleApplication : public QApplication -{ - Q_OBJECT - -public: - QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); - QtSingleApplication(const QString &id, int &argc, char **argv); - QtSingleApplication(int &argc, char **argv, Type type); -#if defined(Q_WS_X11) - QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); - QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); - QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); -#endif - - bool isRunning(); - QString id() const; - - void setActivationWindow(QWidget* aw, bool activateOnMessage = true); - QWidget* activationWindow() const; - - // Obsolete: - void initialize(bool dummy = true) - { isRunning(); Q_UNUSED(dummy) } - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - void activateWindow(); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - void sysInit(const QString &appId = QString()); - QtLocalPeer *peer; - QWidget *actWin; -}; diff --git a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp b/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp deleted file mode 100644 index cf607710e..000000000 --- a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtsinglecoreapplication.h" -#include "qtlocalpeer.h" - -/*! - \class QtSingleCoreApplication qtsinglecoreapplication.h - \brief A variant of the QtSingleApplication class for non-GUI applications. - - This class is a variant of QtSingleApplication suited for use in - console (non-GUI) applications. It is an extension of - QCoreApplication (instead of QApplication). It does not require - the QtGui library. - - The API and usage is identical to QtSingleApplication, except that - functions relating to the "activation window" are not present, for - obvious reasons. Please refer to the QtSingleApplication - documentation for explanation of the usage. - - A QtSingleCoreApplication instance can communicate to a - QtSingleApplication instance if they share the same application - id. Hence, this class can be used to create a light-weight - command-line tool that sends commands to a GUI application. - - \sa QtSingleApplication -*/ - -/*! - Creates a QtSingleCoreApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc and \a - argv are passed on to the QCoreAppliation constructor. -*/ - -QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleCoreApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QCoreAppliation constructor. -*/ -QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleCoreApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleCoreApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ - -bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ - -QString QtSingleCoreApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - \fn void QtSingleCoreApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage() -*/ diff --git a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h b/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h deleted file mode 100644 index ef529a8f6..000000000 --- a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include - -class QtLocalPeer; - -class QtSingleCoreApplication : public QCoreApplication -{ - Q_OBJECT - -public: - QtSingleCoreApplication(int &argc, char **argv); - QtSingleCoreApplication(const QString &id, int &argc, char **argv); - - bool isRunning(); - QString id() const; - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - QtLocalPeer* peer; -}; diff --git a/src/main.cpp b/src/main.cpp index 916ffbb2e..415be1d15 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,8 @@ #endif #include + +#include "kdsingleapplicationguard/kdsingleapplicationguard.h" int main( int argc, char *argv[] ) { @@ -38,15 +40,11 @@ main( int argc, char *argv[] ) AEInstallEventHandler( 'GURL', 'GURL', h, 0, false ); #endif - try - { - TomahawkApp a( argc, argv ); - return a.exec(); - } - catch( const std::runtime_error& e ) - { - return 0; - } + TomahawkApp a( argc, argv ); + KDSingleApplicationGuard guard( &a, KDSingleApplicationGuard::AutoKillOtherInstances ); + QObject::connect( &guard, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ), &a, SLOT( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + + return a.exec(); } #ifdef Q_WS_MAC diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 7a94f11a0..d668cc012 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -151,14 +151,6 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) { qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); - // send the first arg to an already running instance, but don't open twice no matter what - if( ( argc > 1 && sendMessage( argv[ 1 ] ) ) || sendMessage( "" ) ) { - qDebug() << "Sent message, already exists"; - throw runtime_error( "Already Running" ); - } - - connect( this, SIGNAL( messageReceived( QString ) ), this, SLOT( messageReceived( QString ) ) ); - #ifdef TOMAHAWK_HEADLESS m_headless = true; #else @@ -529,13 +521,15 @@ TomahawkApp::loadUrl( const QString& url ) void -TomahawkApp::messageReceived( const QString& msg ) +TomahawkApp::instanceStarted( KDSingleApplicationGuard::Instance instance ) { - qDebug() << "MESSAGE RECEIVED" << msg; - if( msg.isEmpty() ) { + qDebug() << "INSTANCE STARTED!" << instance.pid << instance.arguments; + + if( instance.arguments.size() < 2 ) + { return; } - loadUrl( msg ); + loadUrl( instance.arguments.at( 1 ) ); } diff --git a/thirdparty/jreen b/thirdparty/jreen index 040ca3f3c..126ef9d96 160000 --- a/thirdparty/jreen +++ b/thirdparty/jreen @@ -1 +1 @@ -Subproject commit 040ca3f3cb9b30b4845fc23054c833fda4717460 +Subproject commit 126ef9d96bf774b9808a16dd8c94001af408528b From 37f29966a88d656c11d9e8f2a137697c0c7778b8 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 2 Apr 2011 19:34:24 -0400 Subject: [PATCH 09/63] use our export macro that works on windows :) --- .../kdsingleapplicationguard/kdsingleapplicationguard.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h index f649ef836..14706e4d0 100644 --- a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h +++ b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h @@ -5,6 +5,7 @@ #include #include "pimpl_ptr.h" +#include "dllmacro.h" class QCoreApplication; @@ -12,7 +13,7 @@ class QCoreApplication; void SIGINT_handler( int sig ); #endif -class KDTOOLSCORE_EXPORT KDSingleApplicationGuard : public QObject +class DLLEXPORT KDSingleApplicationGuard : public QObject { Q_OBJECT #ifndef Q_WS_WIN From 40d4636352c72d15e3bc42433847ab1d2de4afab Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 22 Mar 2011 21:33:27 -0400 Subject: [PATCH 10/63] tomahawk:// handler stuff on windows --- admin/win/nsi/tomahawk.nsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi index e9ba96112..28bdb4c59 100644 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -457,6 +457,12 @@ Section -post WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "NoModify" "1" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "NoRepair" "1" + ; Register tomahawk:// protocol handler + WriteRegStr HKCR "tomahawk" "" "URL: Tomahawk Protocol" + WriteRegStr HKCR "tomahawk\DefaultIcon" "" $INSTDIR\tomahawk.exe,1 + WriteRegStr HKCR "tomahawk\shell" "" "open" + WriteRegStr HKCR "tomahawk\shell\open\command" "" '"$INSTDIR\tomahawk.exe" "%1"' + SetDetailsPrint textonly DetailPrint "Finsihed." SectionEnd @@ -516,6 +522,8 @@ Section Uninstall DeleteRegValue HKLM "Software\Tomahawk" "" DeleteRegKey HKLM "Software\Tomahawk" + DeleteRegKey HKCR "tomahawk" + ;Start menu shortcuts. !ifdef OPTION_SECTION_SC_START_MENU SetShellVarContext all From b625b9a2658fd84b6ac1f2f45bcd84020dd12404 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 3 Apr 2011 10:23:05 +0200 Subject: [PATCH 11/63] * Unthread QtScriptResolver. WebKit is too lame for us. --- src/resolvers/qtscriptresolver.cpp | 125 ++++++----------------------- src/resolvers/qtscriptresolver.h | 43 ++-------- 2 files changed, 32 insertions(+), 136 deletions(-) diff --git a/src/resolvers/qtscriptresolver.cpp b/src/resolvers/qtscriptresolver.cpp index a19147b14..4a23439e6 100644 --- a/src/resolvers/qtscriptresolver.cpp +++ b/src/resolvers/qtscriptresolver.cpp @@ -32,81 +32,11 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) { qDebug() << Q_FUNC_INFO << scriptPath; - m_thread = new ScriptThread( scriptPath, this ); - connect( m_thread, SIGNAL( engineFound( QString, unsigned int, unsigned int, unsigned int ) ), - SLOT( onEngineFound( QString, unsigned int, unsigned int, unsigned int ) ) ); - - m_thread->start(); - - connect( this, SIGNAL( destroyed( QObject* ) ), m_thread, SLOT( deleteLater() ) ); -} - - -QtScriptResolver::~QtScriptResolver() -{ - Tomahawk::Pipeline::instance()->removeResolver( this ); - delete m_thread; -} - - -void -QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) -{ - m_thread->resolve( query ); -} - - -void -QtScriptResolver::onEngineFound( const QString& name, unsigned int weight, unsigned int timeout, unsigned int preference ) -{ - m_name = name; - m_weight = weight; - m_timeout = timeout; - m_preference = preference; - - qDebug() << "QTSCRIPT" << filePath() << "READY," << endl - << "name" << m_name << endl - << "weight" << m_weight << endl - << "timeout" << m_timeout << endl - << "preference" << m_preference; - - m_ready = true; - Tomahawk::Pipeline::instance()->addResolver( this ); -} - - -ScriptThread::ScriptThread( const QString& scriptPath, QtScriptResolver* parent ) - : QThread() - , m_parent( parent ) - , m_scriptPath( scriptPath ) -{ - moveToThread( this ); -} - - -void -ScriptThread::resolve( const Tomahawk::query_ptr& query ) -{ - m_engine->resolve( query ); -} - - -void -ScriptThread::run() -{ - QTimer::singleShot( 0, this, SLOT( initEngine() ) ); - exec(); -} - - -void -ScriptThread::initEngine() -{ - m_engine = new ScriptEngine( m_parent, this ); - QFile scriptFile( m_scriptPath ); + m_engine = new ScriptEngine( this ); + QFile scriptFile( scriptPath ); if ( !scriptFile.open( QIODevice::ReadOnly ) ) { - qDebug() << Q_FUNC_INFO << "Failed loading JavaScript resolver:" << m_scriptPath; + qDebug() << Q_FUNC_INFO << "Failed loading JavaScript resolver:" << scriptPath; deleteLater(); return; } @@ -115,42 +45,39 @@ ScriptThread::initEngine() m_engine->mainFrame()->evaluateJavaScript( scriptFile.readAll() ); scriptFile.close(); - QString name; - unsigned int weight, preference, timeout; QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( "getSettings();" ).toMap(); - name = m.value( "name" ).toString(); - weight = m.value( "weight", 0 ).toUInt(); - timeout = m.value( "timeout", 25 ).toUInt() * 1000; - preference = m.value( "preference", 0 ).toUInt(); + m_name = m.value( "name" ).toString(); + m_weight = m.value( "weight", 0 ).toUInt(); + m_timeout = m.value( "timeout", 25 ).toUInt() * 1000; + m_preference = m.value( "preference", 0 ).toUInt(); - qDebug() << Q_FUNC_INFO << name << weight << timeout << preference; - emit engineFound( name, weight, timeout, preference ); + qDebug() << Q_FUNC_INFO << m_name << m_weight << m_timeout << m_preference; + + m_ready = true; + Tomahawk::Pipeline::instance()->addResolver( this ); +} + + +QtScriptResolver::~QtScriptResolver() +{ + Tomahawk::Pipeline::instance()->removeResolver( this ); + delete m_engine; } void -ScriptEngine::resolve( const Tomahawk::query_ptr& query ) +QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) { - if ( QThread::currentThread() != thread() ) - { -// qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; - QMetaObject::invokeMethod( this, "resolve", - Qt::QueuedConnection, - Q_ARG(Tomahawk::query_ptr, query) - ); - return; - } - qDebug() << Q_FUNC_INFO << query->toString(); QString eval = QString( "resolve( '%1', '%2', '%3', '%4' );" ) - .arg( query->id().replace( "'", "\\'" ) ) - .arg( query->artist().replace( "'", "\\'" ) ) - .arg( query->album().replace( "'", "\\'" ) ) - .arg( query->track().replace( "'", "\\'" ) ); + .arg( query->id().replace( "'", "\\'" ) ) + .arg( query->artist().replace( "'", "\\'" ) ) + .arg( query->album().replace( "'", "\\'" ) ) + .arg( query->track().replace( "'", "\\'" ) ); QList< Tomahawk::result_ptr > results; - QVariantMap m = mainFrame()->evaluateJavaScript( eval ).toMap(); + QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( eval ).toMap(); qDebug() << "JavaScript Result:" << m; const QString qid = query->id(); @@ -169,9 +96,9 @@ ScriptEngine::resolve( const Tomahawk::query_ptr& query ) rp->setBitrate( m.value( "bitrate" ).toUInt() ); rp->setUrl( m.value( "url" ).toString() ); rp->setSize( m.value( "size" ).toUInt() ); - rp->setScore( m.value( "score" ).toFloat() * ( (float)m_resolver->weight() / 100.0 ) ); + rp->setScore( m.value( "score" ).toFloat() * ( (float)weight() / 100.0 ) ); rp->setRID( uuid() ); - rp->setFriendlySource( m_resolver->name() ); + rp->setFriendlySource( name() ); if ( m.contains( "year" ) ) { diff --git a/src/resolvers/qtscriptresolver.h b/src/resolvers/qtscriptresolver.h index 7c69fe103..a6850ac96 100644 --- a/src/resolvers/qtscriptresolver.h +++ b/src/resolvers/qtscriptresolver.h @@ -30,7 +30,6 @@ #include #include -class ScriptThread; class QtScriptResolver; class ScriptEngine : public QWebPage @@ -38,18 +37,16 @@ class ScriptEngine : public QWebPage Q_OBJECT public: - explicit ScriptEngine( QtScriptResolver* resolver, ScriptThread* parent ) - : QWebPage( (QObject*)parent ) + explicit ScriptEngine( QtScriptResolver* parent ) + : QWebPage( (QObject*) parent ) , m_parent( parent ) - , m_resolver( resolver ) - {} + { + } public slots: - void resolve( const Tomahawk::query_ptr& query ); - bool shouldInterruptJavaScript() { - return false; + return true; } protected: @@ -57,32 +54,7 @@ protected: { qDebug() << "JAVASCRIPT ERROR:" << message << lineNumber << sourceID; } private: - ScriptThread* m_parent; - QtScriptResolver* m_resolver; -}; - - -class ScriptThread : public QThread -{ -Q_OBJECT - -public: - ScriptThread( const QString& scriptPath, QtScriptResolver* parent ); - - void run(); - - virtual void resolve( const Tomahawk::query_ptr& query ); - -signals: - void engineFound( const QString& name, unsigned int weight, unsigned int timeout, unsigned int preference ); - -private slots: - void initEngine(); - -private: - ScriptEngine* m_engine; QtScriptResolver* m_parent; - QString m_scriptPath; }; @@ -106,11 +78,8 @@ public slots: signals: void finished(); -private slots: - void onEngineFound( const QString& name, unsigned int weight, unsigned int timeout, unsigned int preference ); - private: - ScriptThread* m_thread; + ScriptEngine* m_engine; QString m_name; unsigned int m_weight, m_preference, m_timeout; From 84f6886e8d5e36488c8d83d3686ca34593fef92c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 3 Apr 2011 11:20:35 +0200 Subject: [PATCH 12/63] * Made PlaylistItemDelegate's paint method a lot faster with manual alpha-blending. --- .../playlist/playlistitemdelegate.cpp | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/playlist/playlistitemdelegate.cpp b/src/libtomahawk/playlist/playlistitemdelegate.cpp index d014691c5..ed0f2870b 100644 --- a/src/libtomahawk/playlist/playlistitemdelegate.cpp +++ b/src/libtomahawk/playlist/playlistitemdelegate.cpp @@ -72,14 +72,18 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti if ( !item || item->query().isNull() ) return; + float opacity = 0.0; painter->save(); if ( item->query()->results().count() ) - painter->setOpacity( item->query()->results().at( 0 )->score() ); - else - painter->setOpacity( 0.0 ); + opacity = item->query()->results().first()->score(); - if ( painter->opacity() < 0.3 ) - painter->setOpacity( 0.3 ); + opacity = qMax( (float)0.3, opacity ); + int r = 0, g = 0, b = 0; + r = opacity * r + ( 1 - opacity ) * 255; + g = opacity * g + ( 1 - opacity ) * 255; + b = opacity * b + ( 1 - opacity ) * 255; + + QColor tc( r, g, b ); if ( item->isPlaying() ) { @@ -113,7 +117,14 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti } else { - QStyledItemDelegate::paint( painter, option, index ); + if ( const QStyleOptionViewItem *vioption = qstyleoption_cast(&option)) + { + QStyleOptionViewItemV4 o( *vioption ); + o.palette.setColor( QPalette::Text, tc ); + QStyledItemDelegate::paint( painter, o, index ); + } + else + QStyledItemDelegate::paint( painter, option, index ); } painter->restore(); From 8791a6dbdc02c33923c60237909d7dd1d7bfb9a1 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 3 Apr 2011 11:27:03 +0200 Subject: [PATCH 13/63] * Respect style's default fore / bg colors when painting a PlaylistItem. --- .../playlist/playlistitemdelegate.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/libtomahawk/playlist/playlistitemdelegate.cpp b/src/libtomahawk/playlist/playlistitemdelegate.cpp index ed0f2870b..0edc22e7f 100644 --- a/src/libtomahawk/playlist/playlistitemdelegate.cpp +++ b/src/libtomahawk/playlist/playlistitemdelegate.cpp @@ -77,13 +77,16 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti if ( item->query()->results().count() ) opacity = item->query()->results().first()->score(); - opacity = qMax( (float)0.3, opacity ); - int r = 0, g = 0, b = 0; - r = opacity * r + ( 1 - opacity ) * 255; - g = opacity * g + ( 1 - opacity ) * 255; - b = opacity * b + ( 1 - opacity ) * 255; + QColor textcol, bgcol; + textcol = option.palette.color( QPalette::Foreground ); + bgcol = option.palette.color( QPalette::Background ); - QColor tc( r, g, b ); + opacity = qMax( (float)0.3, opacity ); + int r = textcol.red(), g = textcol.green(), b = textcol.blue(); + r = opacity * r + ( 1 - opacity ) * bgcol.red(); + g = opacity * g + ( 1 - opacity ) * bgcol.green(); + b = opacity * b + ( 1 - opacity ) * bgcol.blue(); + textcol = QColor( r, g, b ); if ( item->isPlaying() ) { @@ -120,7 +123,7 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti if ( const QStyleOptionViewItem *vioption = qstyleoption_cast(&option)) { QStyleOptionViewItemV4 o( *vioption ); - o.palette.setColor( QPalette::Text, tc ); + o.palette.setColor( QPalette::Text, textcol ); QStyledItemDelegate::paint( painter, o, index ); } else From d4569b1d38f33a23d33e5adfa9e00c5aae51a111 Mon Sep 17 00:00:00 2001 From: Steven Robertson Date: Sun, 3 Apr 2011 20:23:32 +0800 Subject: [PATCH 14/63] EnsureTomahawkShutdown not required in uninstaller. --- admin/win/nsi/tomahawk.nsi | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi index 28bdb4c59..e7f1ce251 100644 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -189,14 +189,9 @@ FunctionEnd no_process_${processName}_to_end: !macroend -!macro EnsureTomahawkShutdown un - Function ${un}EnsureTomahawkShutdown - !insertmacro CheckAndConfirmEndProcess "tomahawk.exe" - FunctionEnd -!macroend - -!insertmacro EnsureTomahawkShutdown "" -!insertmacro EnsureTomahawkShutdown "un." +Function EnsureTomahawkShutdown + !insertmacro CheckAndConfirmEndProcess "tomahawk.exe" +FunctionEnd ############################################################################## # # From cffa96a81f8747e8f23fe82e1d2801633cdb3864 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 3 Apr 2011 14:39:56 +0200 Subject: [PATCH 15/63] * Updated Changelog. --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index b25481516..785119397 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,8 @@ Version 0.0.3: marked as preferred. * Don't automatically try to resolve all incoming playback logs. This speeds up importing sources a lot. + * Faster painting of playlists with lots of unresolved tracks. + * The tomahawk:// protocol handler works on Windows now. Version 0.0.2: * Don't reconnect to Jabber if the settings dialog is closed successfully From 1ac61194cf5f6f52085181a1826c8830f22cc83d Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 06:56:33 +0200 Subject: [PATCH 16/63] * Mutex-locked logging - write/flush isn't exactly an atomic operation. --- src/tomahawkapp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index d668cc012..8af6a0457 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -80,6 +80,9 @@ ofstream logfile; void TomahawkLogHandler( QtMsgType type, const char *msg ) { + static QMutex s_mutex; + + QMutexLocker locker( &s_mutex ); switch( type ) { case QtDebugMsg: From 5e9ea36228ed888b1fb56736b257183d8bf201b2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 07:56:56 +0200 Subject: [PATCH 17/63] * Fixed result sorting order, always prefers local results with equal score now. --- src/libtomahawk/query.cpp | 21 ++++++++++++++++----- src/libtomahawk/query.h | 2 ++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/query.cpp b/src/libtomahawk/query.cpp index b8c48e83c..10cf98c81 100644 --- a/src/libtomahawk/query.cpp +++ b/src/libtomahawk/query.cpp @@ -63,7 +63,7 @@ Query::addResults( const QList< Tomahawk::result_ptr >& newresults ) { bool becameSolved = false; { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); m_results.append( newresults ); qStableSort( m_results.begin(), m_results.end(), Query::resultSorter ); @@ -101,7 +101,7 @@ void Query::removeResult( const Tomahawk::result_ptr& result ) { { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); m_results.removeAll( result ); } @@ -121,7 +121,7 @@ Query::onResolvingFinished() QList< result_ptr > Query::results() const { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); return m_results; } @@ -129,7 +129,7 @@ Query::results() const unsigned int Query::numResults() const { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); return m_results.length(); } @@ -149,7 +149,18 @@ Query::id() const bool Query::resultSorter( const result_ptr& left, const result_ptr& right ) { - return left->score() > right->score(); + const float ls = left->score(); + const float rs = right->score(); + + if ( ls == rs ) + { + if ( !left->collection().isNull() && left->collection()->source()->isLocal() ) + return true; + else + return false; + } + + return ls > rs; } diff --git a/src/libtomahawk/query.h b/src/libtomahawk/query.h index bfa2b3d1d..9c84d68bf 100644 --- a/src/libtomahawk/query.h +++ b/src/libtomahawk/query.h @@ -116,6 +116,8 @@ private: QString m_track; int m_duration; QString m_resultHint; + + mutable QMutex m_mutex; }; }; //ns From b25241807492ead68c703bfb32456feec95c0a11 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 07:57:37 +0200 Subject: [PATCH 18/63] * Hopefully fixed dupe-file issue. --- src/musicscanner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index 220baa1a7..db66cb1f5 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -125,8 +125,6 @@ MusicScanner::startScan() DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( m_dir ); connect( cmd, SIGNAL( done( QMap ) ), SLOT( setMtimes( QMap ) ) ); - connect( cmd, SIGNAL( done( QMap ) ), - SLOT( scan() ) ); Database::instance()->enqueue( QSharedPointer(cmd) ); } @@ -135,7 +133,9 @@ MusicScanner::startScan() void MusicScanner::setMtimes( const QMap& m ) { + qDebug() << Q_FUNC_INFO << m.count(); m_dirmtimes = m; + scan(); } From f78df2c087842517db8bb8f8399ee1437237d977 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 07:59:22 +0200 Subject: [PATCH 19/63] * Added assert to catch dupe source. --- src/libtomahawk/network/controlconnection.cpp | 9 ++++++++- src/libtomahawk/source.cpp | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/network/controlconnection.cpp b/src/libtomahawk/network/controlconnection.cpp index 2e122ca96..5baa0455d 100644 --- a/src/libtomahawk/network/controlconnection.cpp +++ b/src/libtomahawk/network/controlconnection.cpp @@ -75,6 +75,13 @@ ControlConnection::setup() { qDebug() << Q_FUNC_INFO << id() << name(); + if ( !m_source.isNull() ) + { + qDebug() << "This source seems to be online already."; + Q_ASSERT( false ); + return; + } + QString friendlyName; if ( Servent::isIPWhitelisted( m_sock->peerAddress() ) ) { @@ -85,7 +92,7 @@ ControlConnection::setup() } else friendlyName = name(); - + // setup source and remote collection for this peer m_source = SourceList::instance()->get( id(), friendlyName ); m_source->setControlConnection( this ); diff --git a/src/libtomahawk/source.cpp b/src/libtomahawk/source.cpp index 59ad94688..54c4394b3 100644 --- a/src/libtomahawk/source.cpp +++ b/src/libtomahawk/source.cpp @@ -30,7 +30,7 @@ using namespace Tomahawk; -Source::Source( int id, const QString &username ) +Source::Source( int id, const QString& username ) : QObject() , m_isLocal( false ) , m_online( false ) @@ -38,7 +38,7 @@ Source::Source( int id, const QString &username ) , m_id( id ) , m_cc( 0 ) { - qDebug() << Q_FUNC_INFO; + qDebug() << Q_FUNC_INFO << id << username; if ( id == 0 ) { @@ -146,6 +146,7 @@ Source::setOnline() { if ( m_online ) return; + m_online = true; // ensure username is in the database DatabaseCommand_addSource* cmd = new DatabaseCommand_addSource( m_username, m_friendlyname ); @@ -153,7 +154,6 @@ Source::setOnline() SLOT( dbLoaded( unsigned int, const QString& ) ) ); Database::instance()->enqueue( QSharedPointer(cmd) ); - m_online = true; emit online(); } From 3ba36fc38e334fc7c5a69e1d46eaa5ef7b80e1f0 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 07:59:32 +0200 Subject: [PATCH 20/63] * Cleaned up debug output, lots. --- ChangeLog | 2 ++ .../databasecommand_loaddynamicplaylist.cpp | 6 +++--- .../databasecommand_loadplaylistentries.cpp | 5 ++--- src/libtomahawk/network/connection.cpp | 17 ++++++---------- src/libtomahawk/network/dbsyncconnection.cpp | 9 ++------- src/libtomahawk/pipeline.cpp | 20 +++++++++---------- src/libtomahawk/playlist.cpp | 2 +- .../playlist/dynamic/DynamicPlaylist.cpp | 6 +++--- src/libtomahawk/utils/animatedsplitter.cpp | 2 +- src/sip/jabber/jabber_p.cpp | 14 ++++++------- 10 files changed, 36 insertions(+), 47 deletions(-) diff --git a/ChangeLog b/ChangeLog index 785119397..3bcf6476b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,8 @@ Version 0.0.3: speeds up importing sources a lot. * Faster painting of playlists with lots of unresolved tracks. * The tomahawk:// protocol handler works on Windows now. + * Fixed launching Tomahawk from Installer with administrative permissions. + * Prefer local results when results' score is equal. Version 0.0.2: * Don't reconnect to Jabber if the settings dialog is closed successfully diff --git a/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp b/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp index eb80312b0..f8b449e61 100644 --- a/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp +++ b/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp @@ -52,9 +52,9 @@ DatabaseCommand_LoadDynamicPlaylist::exec( DatabaseImpl* dbi ) QList< QVariantMap > controls; QString playlist_guid; qDebug() << "Loading controls..." << revisionGuid(); - qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype " - "FROM dynamic_playlist_revision, playlist_revision " - "WHERE dynamic_playlist_revision.guid = "<< revisionGuid() << " AND playlist_revision.guid = dynamic_playlist_revision.guid"; +// qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype " +// "FROM dynamic_playlist_revision, playlist_revision " +// "WHERE dynamic_playlist_revision.guid = "<< revisionGuid() << " AND playlist_revision.guid = dynamic_playlist_revision.guid"; if( controlsQuery.first() ) { playlist_guid = controlsQuery.value( 0 ).toString(); diff --git a/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp b/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp index 20143dd53..f29dc8a40 100644 --- a/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp +++ b/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp @@ -45,7 +45,7 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi ) query_entries.bindValue( ":guid", m_revguid ); query_entries.exec(); - qDebug() << "trying to load entries:" << m_revguid; +// qDebug() << "trying to load entries:" << m_revguid; QString prevrev; QJson::Parser parser; bool ok; @@ -55,7 +55,6 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi ) QVariant v = parser.parse( query_entries.value(0).toByteArray(), &ok ); Q_ASSERT( ok && v.type() == QVariant::List ); //TODO m_guids = v.toStringList(); - // qDebug() << "Entries:" << guids; QString inclause = QString("('%1')").arg(m_guids.join("', '")); @@ -115,5 +114,5 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi ) m_islatest = query_entries_old.value( 1 ).toBool(); } - qDebug() << Q_FUNC_INFO << "entrymap:" << m_entrymap; +// qDebug() << Q_FUNC_INFO << "entrymap:" << m_entrymap; } diff --git a/src/libtomahawk/network/connection.cpp b/src/libtomahawk/network/connection.cpp index afa009713..59342fba9 100644 --- a/src/libtomahawk/network/connection.cpp +++ b/src/libtomahawk/network/connection.cpp @@ -62,16 +62,12 @@ Connection::Connection( Servent* parent ) Connection::~Connection() { - qDebug() << "DTOR connection (super)" << id() << thread(); + qDebug() << "DTOR connection (super)" << id() << thread() << m_sock.isNull(); if( !m_sock.isNull() ) { - qDebug() << "deleteLatering sock" << m_sock; +// qDebug() << "deleteLatering sock" << m_sock; m_sock->deleteLater(); } - else - { - qDebug() << "no valid sock to delete"; - } delete m_statstimer; } @@ -118,7 +114,7 @@ Connection::setFirstMessage( msg_ptr m ) void Connection::shutdown( bool waitUntilSentAll ) { - qDebug() << Q_FUNC_INFO << waitUntilSentAll; + qDebug() << Q_FUNC_INFO << waitUntilSentAll << id(); if ( m_do_shutdown ) { //qDebug() << id() << " already shutting down"; @@ -128,7 +124,7 @@ Connection::shutdown( bool waitUntilSentAll ) m_do_shutdown = true; if ( !waitUntilSentAll ) { - qDebug() << "Shutting down immediately " << id(); +// qDebug() << "Shutting down immediately " << id(); actualShutdown(); } else @@ -146,10 +142,9 @@ Connection::shutdown( bool waitUntilSentAll ) void Connection::actualShutdown() { - qDebug() << Q_FUNC_INFO; + qDebug() << Q_FUNC_INFO << m_actually_shutting_down << id(); if( m_actually_shutting_down ) { - qDebug() << "(already actually shutting down)"; return; } m_actually_shutting_down = true; @@ -159,7 +154,7 @@ Connection::actualShutdown() m_sock->disconnectFromHost(); } - qDebug() << "EMITTING finished()"; +// qDebug() << "EMITTING finished()"; emit finished(); } diff --git a/src/libtomahawk/network/dbsyncconnection.cpp b/src/libtomahawk/network/dbsyncconnection.cpp index a25d8ab8f..b3559088f 100644 --- a/src/libtomahawk/network/dbsyncconnection.cpp +++ b/src/libtomahawk/network/dbsyncconnection.cpp @@ -75,7 +75,7 @@ DBSyncConnection::~DBSyncConnection() void DBSyncConnection::idleTimeout() { - qDebug() << Q_FUNC_INFO << "*************"; + qDebug() << Q_FUNC_INFO; shutdown( true ); } @@ -87,11 +87,6 @@ DBSyncConnection::changeState( State newstate ) m_state = newstate; qDebug() << "DBSYNC State changed from" << s << "to" << newstate; emit stateChanged( newstate, s, "" ); - - if ( newstate == SYNCED ) - { - qDebug() << "Synced :)"; - } } @@ -197,7 +192,7 @@ DBSyncConnection::handleMsg( msg_ptr msg ) msg->is( Msg::DBOP ) && msg->payload() == "ok" ) { - qDebug() << "No ops to apply, we are synced."; +// qDebug() << "No ops to apply, we are synced."; changeState( SYNCED ); // calc the collection stats, to updates the "X tracks" in the sidebar etc // this is done automatically if you run a dbcmd to add files. diff --git a/src/libtomahawk/pipeline.cpp b/src/libtomahawk/pipeline.cpp index 3ffc4a154..b7d538a37 100644 --- a/src/libtomahawk/pipeline.cpp +++ b/src/libtomahawk/pipeline.cpp @@ -102,7 +102,7 @@ Pipeline::resolve( const QList& qlist, bool prioritized ) int i = 0; foreach( const query_ptr& q, qlist ) { - qDebug() << Q_FUNC_INFO << (qlonglong)q.data() << q->toString(); +// qDebug() << Q_FUNC_INFO << (qlonglong)q.data() << q->toString(); if ( !m_qids.contains( q->id() ) ) { m_qids.insert( q->id(), q ); @@ -189,7 +189,7 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results ) if ( decQIDState( q ) == 0 ) { // All resolvers have reported back their results for this query now - qDebug() << "Finished resolving:" << q->toString(); + qDebug() << "Finished resolving:" << q->toString() << q->numResults(); if ( !q->solved() ) q->onResolvingFinished(); @@ -216,7 +216,7 @@ Pipeline::shuntNext() return; } - qDebug() << Q_FUNC_INFO << m_qidsState.count(); +// qDebug() << Q_FUNC_INFO << m_qidsState.count(); // Check if we are ready to dispatch more queries if ( m_qidsState.count() >= CONCURRENT_QUERIES ) return; @@ -246,8 +246,8 @@ Pipeline::shunt( const query_ptr& q ) if ( q->solved() ) { - qDebug() << "Query solved, pipeline aborted:" << q->toString() - << "numresults:" << q->results().length(); +// qDebug() << "Query solved, pipeline aborted:" << q->toString() +// << "numresults:" << q->results().length(); QList< result_ptr > rl; reportResults( q->id(), rl ); @@ -275,7 +275,7 @@ Pipeline::shunt( const query_ptr& q ) lasttimeout = r->timeout(); // resolvers aren't allowed to block in this call: - qDebug() << "Dispatching to resolver" << r->name(); + qDebug() << "Dispatching to resolver" << r->name() << q->toString(); thisResolver = i; r->resolve( q ); @@ -291,7 +291,7 @@ Pipeline::shunt( const query_ptr& q ) if ( thisResolver < m_resolvers.count() ) { incQIDState( q ); - qDebug() << "Shunting in" << lasttimeout << "ms, q:" << q->toString(); +// qDebug() << "Shunting in" << lasttimeout << "ms, q:" << q->toString(); new FuncTimeout( lasttimeout, boost::bind( &Pipeline::shunt, this, q ) ); } } @@ -329,7 +329,7 @@ Pipeline::incQIDState( const Tomahawk::query_ptr& query ) state = m_qidsState.value( query->id() ) + 1; } - qDebug() << Q_FUNC_INFO << "inserting to qidsstate:" << query->id() << state; +// qDebug() << Q_FUNC_INFO << "inserting to qidsstate:" << query->id() << state; m_qidsState.insert( query->id(), state ); return state; @@ -344,12 +344,12 @@ Pipeline::decQIDState( const Tomahawk::query_ptr& query ) int state = m_qidsState.value( query->id() ) - 1; if ( state ) { - qDebug() << Q_FUNC_INFO << "replacing" << query->id() << state; +// qDebug() << Q_FUNC_INFO << "replacing" << query->id() << state; m_qidsState.insert( query->id(), state ); } else { - qDebug() << Q_FUNC_INFO << "removing" << query->id() << state; +// qDebug() << Q_FUNC_INFO << "removing" << query->id() << state; m_qidsState.remove( query->id() ); } diff --git a/src/libtomahawk/playlist.cpp b/src/libtomahawk/playlist.cpp index f27e592e0..1ca21e31e 100644 --- a/src/libtomahawk/playlist.cpp +++ b/src/libtomahawk/playlist.cpp @@ -112,7 +112,7 @@ Playlist::Playlist( const source_ptr& src, , m_lastmodified( lastmod ) , m_shared( shared ) { - qDebug() << Q_FUNC_INFO << "1"; +// qDebug() << Q_FUNC_INFO << "1"; init(); } diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index 554ff5f98..6ab5eb30f 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -253,8 +253,8 @@ DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self ) Q_ASSERT( !author().isNull() ); Q_ASSERT( !author()->collection().isNull() ); // will emit Collection::playlistCreated(...) - qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull(); - qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName(); +// qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull(); +// qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName(); author()->collection()->addDynamicPlaylist( self ); } @@ -431,7 +431,7 @@ QList< dyncontrol_ptr > DynamicPlaylist::variantsToControl( const QList< QVarian QList realControls; foreach( QVariantMap controlV, controlsV ) { dyncontrol_ptr control = GeneratorFactory::createControl( controlV.value( "type" ).toString(), controlV.value( "selectedType" ).toString() ); - qDebug() << "CReating control with data:" << controlV; + qDebug() << "Creating control with data:" << controlV; QJson::QObjectHelper::qvariant2qobject( controlV, control.data() ); realControls << control; } diff --git a/src/libtomahawk/utils/animatedsplitter.cpp b/src/libtomahawk/utils/animatedsplitter.cpp index 3d9607ee3..3ec748052 100644 --- a/src/libtomahawk/utils/animatedsplitter.cpp +++ b/src/libtomahawk/utils/animatedsplitter.cpp @@ -83,7 +83,7 @@ AnimatedSplitter::hide( int index, bool animate ) emit hidden( w ); w->setMinimumHeight( minHeight ); - qDebug() << "animating to:" << w->height() << "from" << minHeight; +// qDebug() << "animating to:" << w->height() << "from" << minHeight; m_animateForward = false; if ( animate ) diff --git a/src/sip/jabber/jabber_p.cpp b/src/sip/jabber/jabber_p.cpp index 462296682..a770934d3 100644 --- a/src/sip/jabber/jabber_p.cpp +++ b/src/sip/jabber/jabber_p.cpp @@ -56,7 +56,7 @@ Jabber_p::Jabber_p( const QString& jid, const QString& password, const QString& if( m_jid.resource().find( "tomahawk" ) == std::string::npos ) { - qDebug() << "!!! Setting your resource to 'tomahawk' prior to logging in to jabber"; +// qDebug() << "!!! Setting your resource to 'tomahawk' prior to logging in to jabber"; m_jid.setResource( QString( "tomahawk%1" ).arg( qrand() ).toStdString() ); } @@ -210,8 +210,8 @@ Jabber_p::sendMsg( const QString& to, const QString& msg ) { if ( QThread::currentThread() != thread() ) { - qDebug() << Q_FUNC_INFO << "invoking in correct thread, not" - << QThread::currentThread(); +// qDebug() << Q_FUNC_INFO << "invoking in correct thread, not" +// << QThread::currentThread(); QMetaObject::invokeMethod( this, "sendMsg", Qt::QueuedConnection, @@ -277,7 +277,7 @@ Jabber_p::addContact( const QString& jid, const QString& msg ) void Jabber_p::onConnect() { - qDebug() << "Connected to the XMPP server"; + qDebug() << "Connected to the XMPP server" << m_jid.full().c_str(); // update jid resource, servers like gtalk use resource binding and may // have changed our requested /resource if ( m_client->resource() != m_jid.resource() ) @@ -287,7 +287,6 @@ Jabber_p::onConnect() emit jidChanged( jidstr ); } - qDebug() << "Connected as:" << m_jid.full().c_str(); emit connected(); } @@ -516,7 +515,7 @@ Jabber_p::handleRoster( const Roster& roster ) for ( ; it != roster.end(); ++it ) { if ( (*it).second->subscription() != S10nBoth ) continue; - qDebug() << (*it).second->jid().c_str() << (*it).second->name().c_str(); +// qDebug() << (*it).second->jid().c_str() << (*it).second->name().c_str(); //printf("JID: %s\n", (*it).second->jid().c_str()); } @@ -539,8 +538,6 @@ Jabber_p::handlePresence( const gloox::Presence& presence ) JID jid = presence.from(); QString fulljid( jid.full().c_str() ); - qDebug() << "* handleRosterPresence" << fulljid << presence.subtype(); - if( jid == m_jid ) return; @@ -562,6 +559,7 @@ Jabber_p::handlePresence( const gloox::Presence& presence ) return; } + qDebug() << "* handleRosterPresence" << fulljid << presence.subtype(); //qDebug() << "handling presence for resource of" << res; //qDebug() << Q_FUNC_INFO << "jid:" << QString::fromStdString(item.jid()) From 89c3f266a7c4dfd34b454d83eb05a4cd343f7ac0 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 25 Mar 2011 10:23:43 +0000 Subject: [PATCH 21/63] * Manually merged stever's tomahawk.nsi patch. --- admin/win/nsi/tomahawk.nsi | 119 ++++++++++++++----------------------- src/main.cpp | 2 +- 2 files changed, 46 insertions(+), 75 deletions(-) diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi index e7f1ce251..e724d3df9 100644 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -4,7 +4,7 @@ ; Some installer script options (comment-out options not required) ;----------------------------------------------------------------------------- ;!define OPTION_LICENSE_AGREEMENT -;!define OPTION_UAC_PLUGIN_ENHANCED +!define OPTION_UAC_PLUGIN_ENHANCED !define OPTION_SECTION_SC_START_MENU !define OPTION_SECTION_SC_DESKTOP !define OPTION_SECTION_SC_QUICK_LAUNCH @@ -58,17 +58,10 @@ InstType Full InstType Minimal CRCCheck On SetCompressor /SOLID lzma +RequestExecutionLevel user ;Now using the UAC plugin. ReserveFile tomahawk.ini ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll" -;The UAC plugin provides an elevated user. -;Otherwise request admin level here. -!ifdef OPTION_UAC_PLUGIN_ENHANCED - RequestExecutionLevel user -!else - RequestExecutionLevel admin -!endif - ;----------------------------------------------------------------------------- ; Include some required header files. ;----------------------------------------------------------------------------- @@ -79,9 +72,7 @@ ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll" !include Memento.nsh ;Remember user selections. !include WinVer.nsh ;Windows version detection. !include WordFunc.nsh ;Used by VersionCompare macro function. -!ifdef OPTION_UAC_PLUGIN_ENHANCED - !include UAC.nsh ;Used by the UAC elevation to install as user or admin. -!endif +!include UAC.nsh ;Used by the UAC elevation to install as user or admin. ;----------------------------------------------------------------------------- ; Memento selections stored in registry. @@ -146,18 +137,12 @@ UninstPage custom un.UnPageUserAppData un.UnPageUserAppDataLeave ############################################################################## Function LaunchTomahawk - !ifdef OPTION_UAC_PLUGIN_ENHANCED - ${UAC.CallFunctionAsUser} LaunchTomahawkAsUser - !else - Exec "$INSTDIR\tomahawk.exe" - !endif + ${UAC.CallFunctionAsUser} LaunchTomahawkAsUser FunctionEnd -!ifdef OPTION_UAC_PLUGIN_ENHANCED Function LaunchTomahawkAsUser Exec "$INSTDIR\tomahawk.exe" FunctionEnd -!endif ############################################################################## # # @@ -260,12 +245,10 @@ Function PageLeaveReinstall Delete $R1 RMDir $INSTDIR no_remove_uninstaller: - StrCmp $R0 "2" +2 0 + StrCmp $R0 "2" 0 +3 + UAC::Unload + Quit BringToFront - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload - Quit - !endif reinst_done: FunctionEnd @@ -380,7 +363,7 @@ SectionGroup "Shortcuts" CreateShortCut "$SMPROGRAMS\Tomahawk\LICENSE.lnk" "$INSTDIR\LICENSE.txt" CreateShortCut "$SMPROGRAMS\Tomahawk\Tomahawk.lnk" "$INSTDIR\tomahawk.exe" CreateShortCut "$SMPROGRAMS\Tomahawk\Release notes.lnk" "$INSTDIR\NOTES.txt" - CreateShortCut "$SMPROGRAMS\Tomahawk\Uninstall.lnk" "$INSTDIR\Uninstall.exe" + CreateShortCut "$SMPROGRAMS\Tomahawk\Uninstall.lnk" "$INSTDIR\uninstall.exe" SetShellVarContext current ${MementoSectionEnd} !endif @@ -424,7 +407,7 @@ Section -post SetDetailsPrint textonly DetailPrint "Writing Uninstaller" SetDetailsPrint listonly - WriteUninstaller $INSTDIR\Uninstall.exe + WriteUninstaller $INSTDIR\uninstall.exe ;Registry keys required for installer version handling and uninstaller. SetDetailsPrint textonly @@ -568,27 +551,25 @@ Function .onInit ${MementoSectionRestore} - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC_Elevate: - UAC::RunElevated - StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? - StrCmp 0 $0 0 UAC_Err ; Error? - StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? - Quit + UAC_Elevate: + UAC::RunElevated + StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? + StrCmp 0 $0 0 UAC_Err ; Error? + StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? + Quit - UAC_Err: - MessageBox MB_ICONSTOP "Unable to elevate, error $0" - Abort + UAC_Err: + MessageBox MB_ICONSTOP "Unable to elevate, error $0" + Abort - UAC_ElevationAborted: - Abort + UAC_ElevationAborted: + Abort - UAC_Success: - StrCmp 1 $3 +4 ;Admin? - StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? - MessageBox MB_ICONSTOP "This installer requires admin access, try again" - goto UAC_Elevate - !endif + UAC_Success: + StrCmp 1 $3 +4 ;Admin? + StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? + MessageBox MB_ICONSTOP "This installer requires admin access, try again" + goto UAC_Elevate ;Prevent multiple instances. System::Call 'kernel32::CreateMutexA(i 0, i 0, t "tomahawkInstaller") i .r1 ?e' @@ -610,15 +591,11 @@ FunctionEnd Function .onInstSuccess ${MementoSectionSave} - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd Function .onInstFailed - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd ############################################################################## @@ -628,27 +605,25 @@ FunctionEnd ############################################################################## Function un.onInit - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC_Elevate: - UAC::RunElevated - StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? - StrCmp 0 $0 0 UAC_Err ; Error? - StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? - Quit + UAC_Elevate: + UAC::RunElevated + StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? + StrCmp 0 $0 0 UAC_Err ; Error? + StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? + Quit - UAC_Err: - MessageBox MB_ICONSTOP "Unable to elevate, error $0" - Abort + UAC_Err: + MessageBox MB_ICONSTOP "Unable to elevate, error $0" + Abort - UAC_ElevationAborted: - Abort + UAC_ElevationAborted: + Abort - UAC_Success: - StrCmp 1 $3 +4 ;Admin? - StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? - MessageBox MB_ICONSTOP "This uninstaller requires admin access, try again" - goto UAC_Elevate - !endif + UAC_Success: + StrCmp 1 $3 +4 ;Admin? + StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? + MessageBox MB_ICONSTOP "This uninstaller requires admin access, try again" + goto UAC_Elevate ;Prevent multiple instances. System::Call 'kernel32::CreateMutexA(i 0, i 0, t "tomahawkUninstaller") i .r1 ?e' @@ -659,13 +634,9 @@ Function un.onInit FunctionEnd Function un.onUnInstSuccess - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd Function un.onUnInstFailed - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd diff --git a/src/main.cpp b/src/main.cpp index 415be1d15..3a6d6936e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,8 +38,8 @@ main( int argc, char *argv[] ) // used for url handler AEEventHandlerUPP h = AEEventHandlerUPP( appleEventHandler ); AEInstallEventHandler( 'GURL', 'GURL', h, 0, false ); - #endif + TomahawkApp a( argc, argv ); KDSingleApplicationGuard guard( &a, KDSingleApplicationGuard::AutoKillOtherInstances ); QObject::connect( &guard, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ), &a, SLOT( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); From 9e20674b8409937bbd2294681d1801ada39b2126 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 09:27:16 +0200 Subject: [PATCH 22/63] * Properly escaped mtimes sql query and removed further debug. --- src/libtomahawk/database/databasecommand_dirmtimes.cpp | 3 ++- src/libtomahawk/playlist/trackmodel.cpp | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.cpp b/src/libtomahawk/database/databasecommand_dirmtimes.cpp index 829a609c8..76b99fb0b 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.cpp +++ b/src/libtomahawk/database/databasecommand_dirmtimes.cpp @@ -44,7 +44,8 @@ DatabaseCommand_DirMtimes::execSelect( DatabaseImpl* dbi ) { query.prepare( QString( "SELECT name, mtime " "FROM dirs_scanned " - "WHERE name LIKE '%1%'" ).arg( m_prefix.replace( '\'',"''" ) ) ); + "WHERE name LIKE :prefix" ) ); + query.bindValue( ":prefix", m_prefix ); query.exec(); } while( query.next() ) diff --git a/src/libtomahawk/playlist/trackmodel.cpp b/src/libtomahawk/playlist/trackmodel.cpp index e3fd2083a..f96d3b026 100644 --- a/src/libtomahawk/playlist/trackmodel.cpp +++ b/src/libtomahawk/playlist/trackmodel.cpp @@ -303,7 +303,7 @@ TrackModel::removeIndex( const QModelIndex& index, bool moreToCome ) { if ( QThread::currentThread() != thread() ) { - qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; +// qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; QMetaObject::invokeMethod( this, "removeIndex", Qt::QueuedConnection, Q_ARG(const QModelIndex, index), @@ -312,8 +312,6 @@ TrackModel::removeIndex( const QModelIndex& index, bool moreToCome ) return; } - qDebug() << Q_FUNC_INFO; - if ( index.column() > 0 ) return; From efaf180b80e3299ee2289e6187e5952acd335d88 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 09:34:17 +0200 Subject: [PATCH 23/63] * Forgot the wildcard match. --- src/libtomahawk/database/databasecommand_dirmtimes.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.cpp b/src/libtomahawk/database/databasecommand_dirmtimes.cpp index 76b99fb0b..5384abfd6 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.cpp +++ b/src/libtomahawk/database/databasecommand_dirmtimes.cpp @@ -45,11 +45,12 @@ DatabaseCommand_DirMtimes::execSelect( DatabaseImpl* dbi ) query.prepare( QString( "SELECT name, mtime " "FROM dirs_scanned " "WHERE name LIKE :prefix" ) ); - query.bindValue( ":prefix", m_prefix ); + query.bindValue( ":prefix", m_prefix + "%" ); query.exec(); } while( query.next() ) { + qDebug() << query.value( 0 ).toString(); mtimes.insert( query.value( 0 ).toString(), query.value( 1 ).toUInt() ); } From e824fcc7b3bde5333652ae2cc9a18d9e0bb9fa51 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 10:01:14 +0200 Subject: [PATCH 24/63] * Gather more debug to fix mtimes issue. --- src/libtomahawk/database/databasecommand_dirmtimes.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.cpp b/src/libtomahawk/database/databasecommand_dirmtimes.cpp index 5384abfd6..5b6034086 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.cpp +++ b/src/libtomahawk/database/databasecommand_dirmtimes.cpp @@ -36,17 +36,24 @@ DatabaseCommand_DirMtimes::exec( DatabaseImpl* dbi ) void DatabaseCommand_DirMtimes::execSelect( DatabaseImpl* dbi ) { + qDebug() << Q_FUNC_INFO << m_prefix << m_update; QMap mtimes; TomahawkSqlQuery query = dbi->newquery(); + if( m_prefix.isEmpty() ) + { query.exec( "SELECT name, mtime FROM dirs_scanned" ); + } else { query.prepare( QString( "SELECT name, mtime " "FROM dirs_scanned " "WHERE name LIKE :prefix" ) ); query.bindValue( ":prefix", m_prefix + "%" ); + + qDebug() << query.lastQuery(); query.exec(); + qDebug() << query.lastQuery(); } while( query.next() ) { From 856824535544aaf7f93f557518feed51072871c8 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 25 Mar 2011 11:43:39 +0000 Subject: [PATCH 25/63] * Preparing release version 0.0.3. --- CMakeLists.txt | 2 +- admin/win/nsi/revision.txt | 2 +- admin/win/nsi/tomahawk.nsi | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) mode change 100755 => 100644 admin/win/nsi/revision.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index d00a02eec..dde40af13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ CMAKE_MINIMUM_REQUIRED( VERSION 2.8 ) SET( ORGANIZATION_NAME "Tomahawk" ) SET( ORGANIZATION_DOMAIN "tomahawk-player.org" ) SET( APPLICATION_NAME "Tomahawk" ) -SET( VERSION "0.0.2" ) +SET( VERSION "0.0.3" ) # set paths diff --git a/admin/win/nsi/revision.txt b/admin/win/nsi/revision.txt old mode 100755 new mode 100644 index c4fbb1cfa..d97edbb29 --- a/admin/win/nsi/revision.txt +++ b/admin/win/nsi/revision.txt @@ -1 +1 @@ -97 \ No newline at end of file +99 \ No newline at end of file diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi index e724d3df9..416944114 100644 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -35,7 +35,7 @@ !define VER_MAJOR "0" !define VER_MINOR "0" -!define VER_BUILD "2" +!define VER_BUILD "3" !define VERSION "${VER_MAJOR}.${VER_MINOR}.${VER_BUILD}" From 81f4ec2337ca33f53643b7ff0baf80ff08e8d7ed Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 25 Mar 2011 12:10:22 +0000 Subject: [PATCH 26/63] * Update revision. --- admin/win/nsi/revision.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/win/nsi/revision.txt b/admin/win/nsi/revision.txt index d97edbb29..105d7d9ad 100644 --- a/admin/win/nsi/revision.txt +++ b/admin/win/nsi/revision.txt @@ -1 +1 @@ -99 \ No newline at end of file +100 \ No newline at end of file From c61813915df7be87cfc06555057ed40db523ff76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20B=C3=A4ume?= Date: Wed, 30 Mar 2011 06:53:54 +0800 Subject: [PATCH 27/63] initial german translation it's not finished, but a first start --- lang/tomahawk_de.ts | 1484 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1484 insertions(+) create mode 100644 lang/tomahawk_de.ts diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts new file mode 100644 index 000000000..1a028aea7 --- /dev/null +++ b/lang/tomahawk_de.ts @@ -0,0 +1,1484 @@ + + + + + AlbumModel + + + Album + Album + + + + + All albums from %1 + Alle Alben von %1 + + + + AudioControls + + + Form + + + + + Prev + + + + + Play + + + + + Pause + + + + + Next + + + + + TextLabel + + + + + Artist + + + + + Album + + + + + Owner + + + + + Time + + + + + Time Left + + + + + Shuffle + + + + + Repeat + + + + + Low + + + + + High + + + + + ClearButton + + + Clear + Leeren + + + + CollectionFlatModel + + + Your Collection + Deine Sammlung + + + + Collection of %1 + Deine Sammlung von %1 + + + + CollectionModel + + + Name + Name + + + + Tracks + Stücke + + + + Duration + Spieldauer + + + + Origin + Quelle + + + + CollectionView + + + &Play + &Abspielen + + + + Add to &Queue + Zur &Warteschlange hinzufügen + + + + This collection is empty. + Diese Sammlung ist leer. + + + + InfoBar + + + InfoBar + + + + + + + TextLabel + + + + + JabberPlugin + + + Add Friend... + Freund hinzufügen… + + + + + Add Friend + Freund hinzufügen + + + + + Enter Jabber ID: + Jabber-ID eingeben: + + + + Jabber_p + + + Authorize User + Benutzer authorisieren + + + + Do you want to grant <b>%1</b> access to your Collection? + Willst du <b>%1</b> wirklich den Zugriff auf deine Sammlung erlauben? + + + + NewPlaylistWidget + + + Enter a title for the new playlist: + Gib einen Titel für die neue Playliste ein: + + + + Tomahawk offers a variety of ways to help you create playlists and find music you enjoy! + Tomahawk bietet verschiedene Wege, Playlisten zu erstellen und Musik zu finden, die du magst! + + + + Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist: + Gib einfach ein Genre oder einen Tagnamen ein und Tomahawk wird dir einige Lieder vorschlagen, um dir zu helfen, eine neue Playliste zu erstellen: + + + + &Create Playlist + Playliste &erstellen + + + + Create a new playlist + Erstelle eine neue Playliste + + + + PlaylistDelegate + + + %1 tracks + %1 Stücke + + + + PlaylistManager + + + All available tracks + Alle verfügbaren Stücke + + + + All available albums + Alle verfügbaren Alben + + + + PlaylistModel + + + A playlist by %1 + Eine Playliste von %1 + + + + you + dir + + + + PlaylistView + + + &Play + &Abspielen + + + + Add to &Queue + In &Warteschlange einreihen + + + + &Delete Items + Elemente &entfernen + + + + &Delete Item + Element &entfernen + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + Die Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! + + + + ProxyDialog + + + Proxy Settings + Proxy-Einstellungen + + + + Host + Rechnername + + + + Port + Port + + + + User + Benutzer + + + + Password + Passwort + + + + Type + Typ + + + + QueueView + + + + Click to show queue + Klicke hier, um die Warteschlange anzuzeigen + + + + Click to hide queue + Klicke hier, um die Warteschlange auszublenden + + + + SearchLineEdit + + + Search + Suchen + + + + SettingsDialog + + + Music Player Settings + Übersetzung eher dürftig + Einstellungen für das Musikabspielprogramm + + + + Jabber + Jabber + + + + Jabber ID: + Jabber-ID: + + + + + Password: + Passwort: + + + + Advanced Jabber Settings + Erweiterte Einstellungen für Jabber + + + + Server: + Server: + + + + Port: + Port: + + + + Network + Netzwerk + + + + Advanced Network Settings + Erweiterte Netzwerkeinstellungen + + + + If you're having difficulty connecting to peers, try setting this to your external IP address/host name and a port number (default 50210). Make sure to forward that port to this machine! + Wenn du Schwierigkeiten hast, zu anderen Leuten zu verbinden, versuche diene externe IP-Addresse/Rechnernamen und eine Portnummer (Standard 50210) hier einzutragen. Stelle sicher, den Port entsprechend an diesen Rechner weiterzuleiten! + + + + Static Host Name: + Statischer Rechnername: + + + + Static Port: + Statischer Port: + + + + Always use static host name/port? (Overrides UPnP discovery/port forwarding) + Statischen Rechnernamen/Port immer benutzen? (Überschreibt UPnP-discovery/Portweiterleitung) + + + + Proxy Settings... + Proxy-Einstellungen… + + + + Playdar HTTP API + Playdar HTTP API + + + + Connect automatically when Tomahawk starts + Automatisch beim Start von Tomahawk verbinden + + + + Use UPnP to establish port forward + Benutze UPnP um die Portweiterleitung zu konfigurieren + + + + Local Music + Lokale Musik + + + + Path to scan for music files: + Pfad zu den Musikdateien: + + + + ... + … + + + + Last.fm + Last.fm + + + + Scrobble tracks to Last.fm + Gespielte Stücke an Last.fm übertragen + + + + Last.fm Login + Last.fm Anmeldung + + + + Username: + Benutzername: + + + + Test Login + Anmeldung Testen + + + + Script Resolvers + + + + + Loaded script resolvers: + + + + + Select Music Folder + Musikordner auswählen + + + + + Failed + Fehlgeschlagen + + + + Success + Erfolgreich + + + + Could not contact server + Konnte den Server nicht erreichen + + + + Load script resolver file + + + + + SourceDelegate + + + Offline + Nicht Verbunden + + + + All available tracks + Alle verfügbaren Stücke + + + + Online + Verbunden + + + + SourceInfoWidget + + + Recent Albums + Aktuelle Alben + + + + Latest Additions to their Collection + Zuletzt zur Sammlung hinzugefügte Stücke + + + + Recently played Tracks + Aktuell gespiele Stücke + + + + Info about %1 + Information über %1 + + + + Your Collection + Deine Sammlung + + + + SourceTreeItem + + + Super Collection + Komplettsammlung + + + + SourceTreeItemWidget + + + Form + + + + + + + + TextLabel + + + + + Off + + + + + Info + + + + + Super Collection + Komplettsammlung + + + + All available tracks + Alle verfügbaren Stücke + + + + Idle + + + + + %L1 tracks + %L1 Stücke + + + + Checking + Teste + + + + Fetching + Hole + + + + Parsing + Parse + + + + Saving + Speichere + + + + Synced + Synchronisiert + + + + Scanning (%L1 tracks) + Durchsuche (%L1 Stücke) + + + + Offline + Nicht Verbunden + + + + SourceTreeView + + + &Load Playlist + &Lade Playliste + + + + &Rename Playlist + Playliste &umbenennen + + + + &Delete Playlist + Playliste &löschen + + + + Tomahawk::DynamicControlList + + + Click to collapse + Klicken zum Zusammenfalten + + + + Tomahawk::DynamicModel + + + + Could not find a playable track. + +Please change the filters or try again. + Konnte kein spielbares Stück finden. + +Bitte ändere den Filter oder versuche es erneut. + + + + Tomahawk::DynamicSetupWidget + + + Type: + Typ: + + + + Generate + Erzeugen + + + + Tomahawk::DynamicView + + + Add some filters above to seed this station! + Füge einige Filter hinzu, um diese Station zu initialisieren! + + + + Press Generate to get started! + Drücke Erzeugen, um zu beginnen! + + + + Add some filters above, and press Generate to get started! + Füge oben einige Filter hinzu und drücke Erzeugen um zu beginnen! + + + + Tomahawk::EchonestControl + + + + + + is + ist + + + + + + + + + Less + Kleiner + + + + + + + + + More + Größer + + + + 0 BPM + + + + + 500 BPM + + + + + 0 secs + 0 s + + + + 3600 secs + 3600 s + + + + -100 dB + + + + + 100 dB + + + + + Major + Dur + + + + Minor + Moll + + + + C + C + + + + C Sharp + Cis + + + + D + D + + + + E Flat + Es + + + + E + E + + + + F + F + + + + F Sharp + Fis + + + + G + G + + + + A Flat + As + + + + A + A + + + + B Flat + stimmt das? + B + + + + B + H + + + + Ascending + Aufsteigend + + + + Descending + Absteigend + + + + Tempo + Tempo + + + + Duration + Dauer + + + + Loudness + Lautstärke + + + + Artist Familiarity + + + + + Artist Hotttnesss + + + + + Song Hotttnesss + + + + + Latitude + Breitengrad + + + + Longitude + Längengrad + + + + Mode + Modus + + + + Key + Schlüssel + + + + Energy + Energie + + + + Danceability + Tanzbarkeit + + + + Tomahawk::EchonestSteerer + + + Steer this station: + Steuere diese Station: + + + + Takes effect on track change + Wird nach dem Wechsel eines Stückes aktiv + + + + Much less + Viel Weniger + + + + Less + Weniger + + + + A bit less + Etwas Weniger + + + + Keep at current + So belassen + + + + A bit more + Etwas Mehr + + + + More + Mehr + + + + Much more + Viel Mehr + + + + Tempo + Tempo + + + + Loudness + Lautstärke + + + + Danceability + Tanzbarkeit + + + + Energy + Energie + + + + Song Hotttnesss + + + + + Artist Hotttnesss + + + + + Artist Familiarity + + + + + By Description + Von der Beschreibung + + + + Enter a description + Gib eine Beschreibung ein + + + + Reset all steering commands + Setze alle Steuerkommandos zurück + + + + Tomahawk::Source + + + + Scanning (%L1 tracks) + Scanne (%L1 Stücke) + + + + Checking + Teste + + + + Fetching + Hole + + + + Parsing + Parse + + + + Saving + Speichere + + + + TomahawkTrayIcon + + + Play + Abspielen + + + + Pause + Pause + + + + Stop + Anhalten + + + + Previous Track + Vorheriges Stück + + + + Next Track + Nächstes Stück + + + + Quit + Verlassen + + + + Currently not playing. + Derzeit wird nichts gespielt. + + + + TomahawkWindow + + + Tomahawk + Tomahawk + + + + &Settings + &Einstellungen + + + + &Music Player + &Abspielprogramm + + + + &Playlist + &Playliste + + + + &Network + &Netzwerk + + + + &Help + &Hilfe + + + + &Quit + &Verlassen + + + + Ctrl+Q + Strg+Q + + + + + Go &online + &Verbindung herstellen + + + + Add &Friend... + Freund &hinzufügen… + + + + Re&scan Collection... + Sammlung neu&laden… + + + + &Configure Tomahawk... + Tomahawk &einrichten… + + + + Load &XSPF... + &XSPF-Datei laden… + + + + Create &New Playlist... + Neue &Playliste erstellen… + + + + About &Tomahawk... + Über &Tomahawk… + + + + Create New &Automatic Playlist + Neue, &automatische Playliste erstellen + + + + Create New &Station + Neue &Station erstellen + + + + Show Offline Sources + Nicht-Verfügbare Quellen anzeigen + + + + Hide Offline Sources + Nicht-Verfügbare Quellen ausblenden + + + + + Check for updates... + Teste auf updates… + + + + Back + Zurück + + + + Forward + Forwärts + + + + Home + + + + + + + Connect To Peer + Zu Gegenstelle verbinden + + + + Enter peer address: + Gib die Adresse der Gegenstelle ein: + + + + Enter peer port: + Gib den Port der Gegenstelle ein: + + + + Enter peer key: + Gib den Schlüssel der Gegenstelle ein: + + + + Go &offline + Verbindung &trennen + + + + Authentication Error + Authentifizierungsfehler + + + + by + von + + + + <h2><b>Tomahawk %1</h2>Copyright 2010, 2011<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter and Steve Robertson + + + + + TopBar + + + Form + + + + + 0 Sources + + + + + 0 Tracks + + + + + 0 Artists + + + + + 0 Shown + + + + + Tracks + Stücke + + + + Artists + Künstler + + + + Sources + Quellen + + + + Shown + Angezeigt + + + + TrackModel + + + Artist + Künstler + + + + Track + Titel + + + + Album + Album + + + + Duration + Spieldauer + + + + Bitrate + Bitrate + + + + Age + Alter + + + + Year + Jahr + + + + Size + Größe + + + + Origin + Quelle + + + + TrackView + + + Sorry, your filter '%1' did not match any results. + Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. + + + + TransferView + + + Peer + Gegenstelle + + + + Rate + Rate + + + + Track + Stück + + + + TwitterConfigWidget + + + Authenticating with Twitter allows you to discover and play music from your Twitter friends running Tomahawk. + + + + + This feature works best when you have set a static host name in the "Network" settings tab under Advanced Settings, but may work even if you do not. Tomahawk uses Direct Messages and this will only work when both Twitter users have followed each other. + + + + + Status: No saved credentials + + + + + Authenticate with Twitter + + + + + Here's how it works: just press one of the buttons below to tweet "Got Tomahawk?" and some necessary information. Then be (very) patient. Twitter is an asynchronous protocol so it can take a bit! + +If connections to peers seem to have been lost, just press the appropriate button again to re-post a tweet for resynchronization. + + + + + Select the kind of tweet you would like, then press the button to post it: + + + + + Global Tweet + + + + + @Mention + + + + + Direct Message + + + + + e.g. @tomahawkplayer + + + + + Tweet! + + + + + WelcomeWidget + + + Recently played playlists: + Aktuell gespielte Playlisten: + + + + Recently played tracks: + Aktuell gespielte Stücke: + + + + You have not played any playlists yet. + Du hast bisher keine Playlisten abgespielt. + + + + Welcome to Tomahawk + Willkommen bei Tomahawk + + + + XSPFLoader + + + New Playlist + Neue Playliste + + + + Failed to save tracks + Konnte Stücke nicht abspeichern + + + + Some tracks in the playlist do not contain an artist and a title. They will be ignored. + Einige Stücke in der Playliste enthalten weder Künstler noch Titel. Diese werden ignoriert. + + + + XSPF Error + XSPF-Fehler + + + + This is not a valid XSPF playlist. + Dies ist keine valide XSPF-Playliste. + + + From 492c2acf18422176bb1cf668cb2cc30f02fb8305 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 30 Mar 2011 19:07:05 +0200 Subject: [PATCH 28/63] i18n: tr() and avoid string puzzle --- src/libtomahawk/network/servent.cpp | 12 +++---- src/sip/jabber/jabber.cpp | 2 +- src/sip/twitter/twitter.cpp | 2 +- src/sip/twitter/twitterconfigwidget.cpp | 46 ++++++++++++------------- src/xmppbot/xmppbot.cpp | 10 +++--- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index 182c8a2f4..1d54d0897 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -676,12 +676,12 @@ Servent::checkACL( const Connection* conn, const QString &nodeid, bool showDialo qDebug() << "ACL for this node not found"; QMessageBox msgBox; msgBox.setIcon( QMessageBox::Question ); - msgBox.setText( "Incoming Connection Attempt" ); - msgBox.setInformativeText( QString( "Another Tomahawk instance is attempting to connect to you. Select whether to allow or deny this connection.\n\nPeer name: %1\nPeer ID: %2\n\nRemember: Only allow peers to connect if you have the legal right for them to stream music from you.").arg( conn->name(), nodeid ) ); - QPushButton *denyButton = msgBox.addButton( "Deny", QMessageBox::HelpRole ); - QPushButton *alwaysDenyButton = msgBox.addButton( "Always Deny", QMessageBox::YesRole ); - QPushButton *allowButton = msgBox.addButton( "Allow", QMessageBox::NoRole ); - QPushButton *alwaysAllowButton = msgBox.addButton( "Always Allow", QMessageBox::ActionRole ); + msgBox.setText( tr( "Incoming Connection Attempt" ) ); + msgBox.setInformativeText( tr( "Another Tomahawk instance is attempting to connect to you. Select whether to allow or deny this connection.\n\nPeer name: %1\nPeer ID: %2\n\nRemember: Only allow peers to connect if you have the legal right for them to stream music from you.").arg( conn->name(), nodeid ) ); + QPushButton *denyButton = msgBox.addButton( tr( "Deny" ), QMessageBox::HelpRole ); + QPushButton *alwaysDenyButton = msgBox.addButton( tr( "Always Deny" ), QMessageBox::YesRole ); + QPushButton *allowButton = msgBox.addButton( tr( "Allow" ), QMessageBox::NoRole ); + QPushButton *alwaysAllowButton = msgBox.addButton( tr( "Always Allow" ), QMessageBox::ActionRole ); msgBox.setDefaultButton( denyButton ); msgBox.setEscapeButton( denyButton ); diff --git a/src/sip/jabber/jabber.cpp b/src/sip/jabber/jabber.cpp index ce62909c4..dab22ac5b 100644 --- a/src/sip/jabber/jabber.cpp +++ b/src/sip/jabber/jabber.cpp @@ -120,7 +120,7 @@ JabberPlugin::onConnected() { if ( !m_menu ) { - m_menu = new QMenu( QString( "Jabber (" ).append( accountName() ).append( ")" ) ); + m_menu = new QMenu( tr( "Jabber (%1)" ).arg( accountName() ) ); m_addFriendAction = m_menu->addAction( tr( "Add Friend..." ) ); connect( m_addFriendAction, SIGNAL( triggered() ), SLOT( showAddFriendDialog() ) ) ; diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index 655dd832b..772c9abdb 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -87,7 +87,7 @@ TwitterPlugin::name() const QString TwitterPlugin::friendlyName() { - return QString("Twitter"); + return tr("Twitter"); } const QString diff --git a/src/sip/twitter/twitterconfigwidget.cpp b/src/sip/twitter/twitterconfigwidget.cpp index 85660318a..8211f97dc 100644 --- a/src/sip/twitter/twitterconfigwidget.cpp +++ b/src/sip/twitter/twitterconfigwidget.cpp @@ -51,8 +51,8 @@ TwitterConfigWidget::TwitterConfigWidget( SipPlugin* plugin, QWidget *parent ) : TomahawkSettings* s = TomahawkSettings::instance(); if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() || s->twitterScreenName().isEmpty() ) { - ui->twitterStatusLabel->setText("Status: No saved credentials"); - ui->twitterAuthenticateButton->setText( "Authenticate" ); + ui->twitterStatusLabel->setText( tr( "Status: No saved credentials" ) ); + ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( false ); ui->twitterGlobalTweetLabel->setVisible( false ); ui->twitterTweetGotTomahawkButton->setVisible( false ); @@ -63,8 +63,8 @@ TwitterConfigWidget::TwitterConfigWidget( SipPlugin* plugin, QWidget *parent ) : } else { - ui->twitterStatusLabel->setText("Status: Credentials saved for " + s->twitterScreenName() ); - ui->twitterAuthenticateButton->setText( "De-authenticate" ); + ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( s->twitterScreenName() ) ); + ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( true ); ui->twitterGlobalTweetLabel->setVisible( true ); ui->twitterTweetGotTomahawkButton->setVisible( true ); @@ -84,7 +84,7 @@ TwitterConfigWidget::~TwitterConfigWidget() void TwitterConfigWidget::authDeauthTwitter() { - if ( ui->twitterAuthenticateButton->text() == "Authenticate" ) + if ( ui->twitterAuthenticateButton->text() == tr( "Authenticate" ) ) //FIXME: don't rely on UI strings here! authenticateTwitter(); else deauthenticateTwitter(); @@ -114,7 +114,7 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) qDebug() << Q_FUNC_INFO; if ( user.id() == 0 ) { - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("The credentials could not be verified.\nYou may wish to try re-authenticating.") ); + QMessageBox::critical( 0, tr("Tweetin' Error"), tr("The credentials could not be verified.\nYou may wish to try re-authenticating.") ); emit twitterAuthed( false ); return; } @@ -124,8 +124,8 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) s->setTwitterCachedFriendsSinceId( 0 ); s->setTwitterCachedMentionsSinceId( 0 ); - ui->twitterStatusLabel->setText("Status: Credentials saved for " + s->twitterScreenName() ); - ui->twitterAuthenticateButton->setText( "De-authenticate" ); + ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( s->twitterScreenName() ) ); + ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( true ); ui->twitterGlobalTweetLabel->setVisible( true ); ui->twitterTweetGotTomahawkButton->setVisible( true ); @@ -142,7 +142,7 @@ TwitterConfigWidget::authenticateVerifyError( QTweetNetBase::ErrorCode code, con { qDebug() << Q_FUNC_INFO; qDebug() << "Error validating credentials, error code is " << code << ", error message is " << errorMsg; - ui->twitterStatusLabel->setText("Status: Error validating credentials"); + ui->twitterStatusLabel->setText(tr("Status: Error validating credentials")); emit twitterAuthed( false ); return; } @@ -156,8 +156,8 @@ TwitterConfigWidget::deauthenticateTwitter() s->setTwitterOAuthTokenSecret( QString() ); s->setTwitterScreenName( QString() ); - ui->twitterStatusLabel->setText("Status: No saved credentials"); - ui->twitterAuthenticateButton->setText( "Authenticate" ); + ui->twitterStatusLabel->setText(tr("Status: No saved credentials")); + ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( false ); ui->twitterGlobalTweetLabel->setVisible( false ); ui->twitterTweetGotTomahawkButton->setVisible( false ); @@ -170,7 +170,7 @@ TwitterConfigWidget::deauthenticateTwitter() void TwitterConfigWidget::tweetComboBoxIndexChanged( int index ) { - if( ui->twitterTweetComboBox->currentText() == "Global Tweet" ) + if( ui->twitterTweetComboBox->currentText() == tr( "Global Tweet" ) ) //FIXME: use data! { ui->twitterUserTweetLineEdit->setReadOnly( true ); ui->twitterUserTweetLineEdit->setEnabled( false ); @@ -181,10 +181,10 @@ TwitterConfigWidget::tweetComboBoxIndexChanged( int index ) ui->twitterUserTweetLineEdit->setEnabled( true ); } - if( ui->twitterTweetComboBox->currentText() == "Direct Message" ) - ui->twitterTweetGotTomahawkButton->setText( "Send Message!" ); + if( ui->twitterTweetComboBox->currentText() == tr( "Direct Message" ) ) //FIXME: use data! + ui->twitterTweetGotTomahawkButton->setText( tr( "Send Message!" ) ); else - ui->twitterTweetGotTomahawkButton->setText( "Tweet!" ); + ui->twitterTweetGotTomahawkButton->setText( tr( "Tweet!" ) ); } void @@ -194,7 +194,7 @@ TwitterConfigWidget::startPostGotTomahawkStatus() if ( m_postGTtype != "Global Tweet" && ( ui->twitterUserTweetLineEdit->text().isEmpty() || ui->twitterUserTweetLineEdit->text() == "@" ) ) { - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("You must enter a user name for this type of tweet.") ); + QMessageBox::critical( 0, tr("Tweetin' Error"), tr("You must enter a user name for this type of tweet.") ); return; } @@ -202,7 +202,7 @@ TwitterConfigWidget::startPostGotTomahawkStatus() TomahawkSettings* s = TomahawkSettings::instance(); if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() || s->twitterScreenName().isEmpty() ) { - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("Your saved credentials could not be loaded.\nYou may wish to try re-authenticating.") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("Your saved credentials could not be loaded.\nYou may wish to try re-authenticating.") ); emit twitterAuthed( false ); return; } @@ -220,7 +220,7 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use { if ( user.id() == 0 ) { - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("Your saved credentials could not be verified.\nYou may wish to try re-authenticating.") ); + QMessageBox::critical( 0, tr("Tweetin' Error"), tr("Your saved credentials could not be verified.\nYou may wish to try re-authenticating.") ); emit twitterAuthed( false ); return; } @@ -264,18 +264,18 @@ void TwitterConfigWidget::postGotTomahawkStatusUpdateReply( const QTweetStatus& status ) { if ( status.id() == 0 ) - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("There was an error posting your status -- sorry!") ); + QMessageBox::critical( 0, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); else - QMessageBox::information( 0, QString("Tweeted!"), QString("Your tweet has been posted!") ); + QMessageBox::information( 0, tr("Tweeted!"), tr("Your tweet has been posted!") ); } void TwitterConfigWidget::postGotTomahawkDirectMessageReply( const QTweetDMStatus& status ) { if ( status.id() == 0 ) - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("There was an error posting your direct message -- sorry!") ); + QMessageBox::critical( 0, tr("Tweetin' Error"), tr("There was an error posting your direct message -- sorry!") ); else - QMessageBox::information( 0, QString("Tweeted!"), QString("Your message has been posted!") ); + QMessageBox::information( 0, tr("Tweeted!"), tr("Your message has been posted!") ); } void @@ -283,5 +283,5 @@ TwitterConfigWidget::postGotTomahawkStatusUpdateError( QTweetNetBase::ErrorCode { qDebug() << Q_FUNC_INFO; qDebug() << "Error posting Got Tomahawk message, error code is " << code << ", error message is " << errorMsg; - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("There was an error posting your status -- sorry!") ); + QMessageBox::critical( 0, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); } diff --git a/src/xmppbot/xmppbot.cpp b/src/xmppbot/xmppbot.cpp index e6be8a57c..d7d267f9d 100644 --- a/src/xmppbot/xmppbot.cpp +++ b/src/xmppbot/xmppbot.cpp @@ -306,9 +306,9 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty } InfoGenericMap tmap = output.value(); QString artist = input.toString(); - m_currReturnMessage += QString("\nTerms for %1:\n").arg(artist); + m_currReturnMessage += tr("\nTerms for %1:\n").arg(artist); if (tmap.isEmpty()) - m_currReturnMessage += QString("No terms found, sorry."); + m_currReturnMessage += tr("No terms found, sorry."); else { bool first = true; @@ -341,7 +341,7 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty QString artist = input.toString(); qreal retVal = output.toReal(); QString retValString = (retVal == 0.0 ? "(none)" : QString::number(retVal)); - m_currReturnMessage += QString("\nHotttness for %1: %2\n").arg(artist).arg(retValString); + m_currReturnMessage += tr("\nHotttness for %1: %2\n").arg(artist, retValString); break; } case InfoArtistFamiliarity: @@ -357,7 +357,7 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty QString artist = input.toString(); qreal retVal = output.toReal(); QString retValString = (retVal == 0.0 ? "(none)" : QString::number(retVal)); - m_currReturnMessage += QString("\nFamiliartiy for %1: %2\n").arg(artist).arg(retValString); + m_currReturnMessage += tr("\nFamiliarity for %1: %2\n").arg(artist, retValString); break; } case InfoTrackLyrics: @@ -375,7 +375,7 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty QString track = inHash["trackName"].toString(); QString lyrics = output.toString(); qDebug() << "lyrics = " << lyrics; - m_currReturnMessage += QString("\nLyrics for \"%1\" by %2:\n\n%3\n").arg(track).arg(artist).arg(lyrics); + m_currReturnMessage += tr("\nLyrics for \"%1\" by %2:\n\n%3\n").arg(track, artist, lyrics); break; } default: From b6d696928156c6abb78777d491e5c40112d0c710 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 30 Mar 2011 18:14:29 +0200 Subject: [PATCH 29/63] pass parent to messageboxes --- src/sip/SipHandler.cpp | 6 +++--- src/sip/twitter/twitterconfigwidget.cpp | 16 ++++++++-------- src/tomahawkwindow.cpp | 12 ++++++------ .../tomahawk-custom/tomahawkoauthtwitter.cpp | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/sip/SipHandler.cpp b/src/sip/SipHandler.cpp index b07eb796c..59aa8ea12 100644 --- a/src/sip/SipHandler.cpp +++ b/src/sip/SipHandler.cpp @@ -182,9 +182,9 @@ SipHandler::connectPlugins( bool startup, const QString &pluginName ) if ( !TomahawkSettings::instance()->acceptedLegalWarning() ) { int result = QMessageBox::question( - TomahawkApp::instance()->mainWindow(), "Legal Warning", - "By pressing OK below, you agree that your use of Tomahawk will be in accordance with any applicable laws, including copyright and intellectual property laws, in effect in your country of residence, and indemnify the Tomahawk developers and project from liability should you choose to break those laws.\n\nFor more information, please see http://gettomahawk.com/legal", - "I Do Not Agree", "I Agree" + TomahawkApp::instance()->mainWindow(), tr( "Legal Warning" ), + tr( "By pressing OK below, you agree that your use of Tomahawk will be in accordance with any applicable laws, including copyright and intellectual property laws, in effect in your country of residence, and indemnify the Tomahawk developers and project from liability should you choose to break those laws.\n\nFor more information, please see http://gettomahawk.com/legal" ), + tr( "I Do Not Agree" ), tr( "I Agree" ) ); if ( result != 1 ) return; diff --git a/src/sip/twitter/twitterconfigwidget.cpp b/src/sip/twitter/twitterconfigwidget.cpp index 8211f97dc..00eb46d87 100644 --- a/src/sip/twitter/twitterconfigwidget.cpp +++ b/src/sip/twitter/twitterconfigwidget.cpp @@ -114,7 +114,7 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) qDebug() << Q_FUNC_INFO; if ( user.id() == 0 ) { - QMessageBox::critical( 0, tr("Tweetin' Error"), tr("The credentials could not be verified.\nYou may wish to try re-authenticating.") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("The credentials could not be verified.\nYou may wish to try re-authenticating.") ); emit twitterAuthed( false ); return; } @@ -194,7 +194,7 @@ TwitterConfigWidget::startPostGotTomahawkStatus() if ( m_postGTtype != "Global Tweet" && ( ui->twitterUserTweetLineEdit->text().isEmpty() || ui->twitterUserTweetLineEdit->text() == "@" ) ) { - QMessageBox::critical( 0, tr("Tweetin' Error"), tr("You must enter a user name for this type of tweet.") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("You must enter a user name for this type of tweet.") ); return; } @@ -220,7 +220,7 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use { if ( user.id() == 0 ) { - QMessageBox::critical( 0, tr("Tweetin' Error"), tr("Your saved credentials could not be verified.\nYou may wish to try re-authenticating.") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("Your saved credentials could not be verified.\nYou may wish to try re-authenticating.") ); emit twitterAuthed( false ); return; } @@ -264,18 +264,18 @@ void TwitterConfigWidget::postGotTomahawkStatusUpdateReply( const QTweetStatus& status ) { if ( status.id() == 0 ) - QMessageBox::critical( 0, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); else - QMessageBox::information( 0, tr("Tweeted!"), tr("Your tweet has been posted!") ); + QMessageBox::information( this, tr("Tweeted!"), tr("Your tweet has been posted!") ); } void TwitterConfigWidget::postGotTomahawkDirectMessageReply( const QTweetDMStatus& status ) { if ( status.id() == 0 ) - QMessageBox::critical( 0, tr("Tweetin' Error"), tr("There was an error posting your direct message -- sorry!") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your direct message -- sorry!") ); else - QMessageBox::information( 0, tr("Tweeted!"), tr("Your message has been posted!") ); + QMessageBox::information( this, tr("Tweeted!"), tr("Your message has been posted!") ); } void @@ -283,5 +283,5 @@ TwitterConfigWidget::postGotTomahawkStatusUpdateError( QTweetNetBase::ErrorCode { qDebug() << Q_FUNC_INFO; qDebug() << "Error posting Got Tomahawk message, error code is " << code << ", error message is " << errorMsg; - QMessageBox::critical( 0, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); } diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 868f1609b..a81395193 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -380,7 +380,7 @@ void TomahawkWindow::loadSpiff() { bool ok; - QString urlstr = QInputDialog::getText( this, "Load XSPF", "Path:", QLineEdit::Normal, "http://ws.audioscrobbler.com/1.0/tag/metal/toptracks.xspf", &ok ); + QString urlstr = QInputDialog::getText( this, tr( "Load XSPF" ), tr( "Path:" ), QLineEdit::Normal, "http://ws.audioscrobbler.com/1.0/tag/metal/toptracks.xspf", &ok ); if ( !ok || urlstr.isEmpty() ) return; @@ -395,7 +395,7 @@ void TomahawkWindow::createAutomaticPlaylist() { bool ok; - QString name = QInputDialog::getText( this, "Create New Automatic Playlist", "Name:", QLineEdit::Normal, "New Automatic Playlist", &ok ); + QString name = QInputDialog::getText( this, tr( "Create New Automatic Playlist" ), tr( "Name:" ), QLineEdit::Normal, tr( "New Automatic Playlist" ), &ok ); if ( !ok || name.isEmpty() ) return; @@ -414,7 +414,7 @@ void TomahawkWindow::createStation() { bool ok; - QString name = QInputDialog::getText( this, "Create New Station", "Name:", QLineEdit::Normal, "New Station", &ok ); + QString name = QInputDialog::getText( this, tr( "Create New Station" ), tr( "Name:" ), QLineEdit::Normal, tr( "New Station" ), &ok ); if ( !ok || name.isEmpty() ) return; @@ -493,8 +493,8 @@ TomahawkWindow::setWindowTitle( const QString& title ) QMainWindow::setWindowTitle( title ); else { - QString s = m_currentTrack->track() + " " + tr( "by" ) + " " + m_currentTrack->artist()->name(); - QMainWindow::setWindowTitle( s + " - " + title ); + QString s = tr( "%1 by %2", "track, artist name" ).arg( m_currentTrack->track(), m_currentTrack->artist()->name() ); + QMainWindow::setWindowTitle( tr( "%1 - %2", "current track, some window title" ).arg( s, title ) ); } } @@ -502,7 +502,7 @@ TomahawkWindow::setWindowTitle( const QString& title ) void TomahawkWindow::showAboutTomahawk() { - QMessageBox::about( this, "About Tomahawk", + QMessageBox::about( this, tr( "About Tomahawk" ), tr( "

Tomahawk %1

Copyright 2010, 2011
Christian Muehlhaeuser <muesli@tomahawk-player.org>

" "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter and Steve Robertson" ) .arg( qApp->applicationVersion() ) ); diff --git a/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp b/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp index 0d4c3e06b..8f2549643 100644 --- a/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp +++ b/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp @@ -12,7 +12,7 @@ int TomahawkOAuthTwitter::authorizationWidget() { bool ok; - int i = QInputDialog::getInt(0, QString( "Twitter PIN" ), QString( "After authenticating on Twitter's web site,\nenter the displayed PIN number here:" ), 0, 0, 2147483647, 1, &ok); + int i = QInputDialog::getInt(0, tr( "Twitter PIN" ), tr( "After authenticating on Twitter's web site,\nenter the displayed PIN number here:" ), 0, 0, 2147483647, 1, &ok); if (ok) return i; From a05795bf6bcf4015151f57fb2fcfb068de2fe8c6 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 30 Mar 2011 18:23:53 +0200 Subject: [PATCH 30/63] fix case --- src/tomahawkwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index a81395193..843d8b525 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -147,7 +147,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) toolbar->installEventFilter( new WidgetDragFilter( toolbar ) ); #if defined( Q_OS_DARWIN ) && defined( HAVE_SPARKLE ) - QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check for updates...") ); + QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check For Updates...") ); checkForUpdates->setMenuRole( QAction::ApplicationSpecificRole ); connect(checkForUpdates, SIGNAL( triggered( bool ) ), SLOT( checkForUpdates() ) ); #elif defined( WIN32 ) @@ -163,7 +163,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) updater->SetVersion( VERSION ); ui->menu_Help->addSeparator(); - QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check for updates...") ); + QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check For Updates...") ); connect( checkForUpdates, SIGNAL( triggered() ), updater, SLOT( CheckNow() ) ); #endif From 56deed391e48357b3a094f4c98c5473bdc40d8c9 Mon Sep 17 00:00:00 2001 From: Steven Robertson Date: Thu, 31 Mar 2011 00:14:20 +0100 Subject: [PATCH 31/63] Added check for running tomahawk.exe process and option to kill the process (or ignore) and proceed. --- admin/win/README.txt | 0 admin/win/nsi/RELEASE_NOTES.txt | 0 admin/win/nsi/installer.ico | Bin .../win/nsi/nsis_processes/bin/Processes.dll | Bin 0 -> 36352 bytes admin/win/nsi/nsis_processes/license.rtf | 35 ++ admin/win/nsi/nsis_processes/readme.txt | 122 ++++++ admin/win/nsi/nsis_processes/src/StdAfx.cpp | 8 + admin/win/nsi/nsis_processes/src/StdAfx.h | 34 ++ admin/win/nsi/nsis_processes/src/exdll.c | 37 ++ admin/win/nsi/nsis_processes/src/exdll.h | 136 ++++++ .../win/nsi/nsis_processes/src/processes.cpp | 411 ++++++++++++++++++ admin/win/nsi/nsis_processes/src/processes.h | 49 +++ .../win/nsi/nsis_processes/src/processes.ncb | Bin 0 -> 44032 bytes admin/win/nsi/nsis_processes/src/processes.rc | 103 +++++ .../win/nsi/nsis_processes/src/processes.sln | 21 + .../win/nsi/nsis_processes/src/processes.txt | 122 ++++++ .../nsi/nsis_processes/src/processes.vcproj | 222 ++++++++++ admin/win/nsi/nsis_processes/src/resource.h | 15 + admin/win/nsi/page_header.bmp | Bin admin/win/nsi/revision.txt | 2 +- admin/win/nsi/tomahawk.ini | 0 admin/win/nsi/tomahawk.nsi | 42 ++ admin/win/nsi/welcome.bmp | Bin admin/win/page_header.bmp | Bin 25818 -> 0 bytes admin/win/welcome.bmp | Bin 154542 -> 0 bytes 25 files changed, 1358 insertions(+), 1 deletion(-) delete mode 100755 admin/win/README.txt mode change 100755 => 100644 admin/win/nsi/RELEASE_NOTES.txt mode change 100755 => 100644 admin/win/nsi/installer.ico create mode 100755 admin/win/nsi/nsis_processes/bin/Processes.dll create mode 100755 admin/win/nsi/nsis_processes/license.rtf create mode 100755 admin/win/nsi/nsis_processes/readme.txt create mode 100755 admin/win/nsi/nsis_processes/src/StdAfx.cpp create mode 100755 admin/win/nsi/nsis_processes/src/StdAfx.h create mode 100755 admin/win/nsi/nsis_processes/src/exdll.c create mode 100755 admin/win/nsi/nsis_processes/src/exdll.h create mode 100755 admin/win/nsi/nsis_processes/src/processes.cpp create mode 100755 admin/win/nsi/nsis_processes/src/processes.h create mode 100755 admin/win/nsi/nsis_processes/src/processes.ncb create mode 100755 admin/win/nsi/nsis_processes/src/processes.rc create mode 100755 admin/win/nsi/nsis_processes/src/processes.sln create mode 100755 admin/win/nsi/nsis_processes/src/processes.txt create mode 100755 admin/win/nsi/nsis_processes/src/processes.vcproj create mode 100755 admin/win/nsi/nsis_processes/src/resource.h mode change 100755 => 100644 admin/win/nsi/page_header.bmp mode change 100644 => 100755 admin/win/nsi/revision.txt mode change 100755 => 100644 admin/win/nsi/tomahawk.ini mode change 100755 => 100644 admin/win/nsi/tomahawk.nsi mode change 100755 => 100644 admin/win/nsi/welcome.bmp delete mode 100755 admin/win/page_header.bmp delete mode 100755 admin/win/welcome.bmp diff --git a/admin/win/README.txt b/admin/win/README.txt deleted file mode 100755 index e69de29bb..000000000 diff --git a/admin/win/nsi/RELEASE_NOTES.txt b/admin/win/nsi/RELEASE_NOTES.txt old mode 100755 new mode 100644 diff --git a/admin/win/nsi/installer.ico b/admin/win/nsi/installer.ico old mode 100755 new mode 100644 diff --git a/admin/win/nsi/nsis_processes/bin/Processes.dll b/admin/win/nsi/nsis_processes/bin/Processes.dll new file mode 100755 index 0000000000000000000000000000000000000000..e532bf8bb55a5b7cadfe6d6e05058ae3f446739a GIT binary patch literal 36352 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≦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 literal 0 HcmV?d00001 diff --git a/admin/win/nsi/nsis_processes/license.rtf b/admin/win/nsi/nsis_processes/license.rtf new file mode 100755 index 000000000..2ce5a58c9 --- /dev/null +++ b/admin/win/nsi/nsis_processes/license.rtf @@ -0,0 +1,35 @@ +{\rtf1\ansi\ansicpg1252\uc1\deff0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f39\fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Verdana;} +{\f172\froman\fcharset238\fprq2 Times New Roman CE;}{\f173\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f175\froman\fcharset161\fprq2 Times New Roman Greek;}{\f176\froman\fcharset162\fprq2 Times New Roman Tur;} +{\f177\froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f178\froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f179\froman\fcharset186\fprq2 Times New Roman Baltic;}{\f180\froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\f562\fswiss\fcharset238\fprq2 Verdana CE;}{\f563\fswiss\fcharset204\fprq2 Verdana Cyr;}{\f565\fswiss\fcharset161\fprq2 Verdana Greek;}{\f566\fswiss\fcharset162\fprq2 Verdana Tur;}{\f569\fswiss\fcharset186\fprq2 Verdana Baltic;} +{\f570\fswiss\fcharset163\fprq2 Verdana (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255; +\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\stylesheet{ +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 Normal;}{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \snext11 \ssemihidden Normal Table;}{\*\cs15 \additive \ul\cf2 \sbasedon10 \styrsid7485074 Hyperlink;}} +{\*\latentstyles\lsdstimax156\lsdlockeddef0}{\*\rsidtbl \rsid6712196\rsid7485074\rsid11352300\rsid15940516}{\*\generator Microsoft Word 11.0.5604;}{\info{\title Processes v1}{\author Hardwired}{\operator Hardwired}{\creatim\yr2004\mo12\dy12\hr23\min42} +{\revtim\yr2004\mo12\dy12\hr23\min51}{\version2}{\edmins9}{\nofpages1}{\nofwords80}{\nofchars458}{\nofcharsws537}{\vern24689}}\widowctrl\ftnbj\aenddoc\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180 +\dgvspace180\dghorigin1800\dgvorigin1440\dghshow1\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct +\asianbrkrule\rsidroot7485074\newtblstyruls\nogrowautofit \fet0\sectd \linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}} +{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (} +{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain +\qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7485074 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\b\f39\insrsid7485074\charrsid7485074 Processes v1.0}{\f39\insrsid7485074\charrsid7485074 .0.1 +\par }{\f39\fs20\insrsid7485074 +\par }\pard \qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid15940516 {\f39\fs20\insrsid15940516 This software binaries and source-code are free for any kind of use, including commercial use. }{ +\f39\fs20\insrsid7485074\charrsid7485074 There is no restriction and no guaranty for using}{\f39\fs20\insrsid7485074\charrsid7485074 t}{\f39\fs20\insrsid7485074\charrsid7485074 his software}{\f39\fs20\insrsid7485074\charrsid7485074 and/or it +s source-code. }{\f39\fs20\insrsid15940516 +\par I}{\f39\fs20\insrsid7485074\charrsid7485074 f you use the plug}{\f39\fs20\insrsid7485074\charrsid7485074 -}{\f39\fs20\insrsid7485074\charrsid7485074 in }{\f39\fs20\insrsid7485074\charrsid7485074 and/}{\f39\fs20\insrsid7485074\charrsid7485074 or it}{ +\f39\fs20\insrsid7485074\charrsid7485074 s}{\f39\fs20\insrsid7485074\charrsid7485074 source-code, I would }{\f39\fs20\insrsid7485074\charrsid7485074 appreciate }{\f39\fs20\insrsid7485074\charrsid7485074 if my name is mentioned.}{ +\f39\fs20\insrsid7485074\charrsid7485074 +\par }\pard \qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7485074 {\f39\fs20\insrsid7485074\charrsid7485074 +\par }{\b\f39\fs20\insrsid7485074\charrsid7485074 Andrei Ciubotaru [Hardwired] +\par }{\f39\fs20\insrsid7485074\charrsid7485074 Lead Developer ICode&Ideas SRL (}{\field\flddirty{\*\fldinst {\f39\fs20\insrsid7485074\charrsid7485074 HYPERLINK "http://www.icode.ro/" }{\f39\fs20\insrsid7485074\charrsid7485074 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b02000000170000001500000068007400740070003a002f002f007700770077002e00690063006f00640065002e0072006f002f000000e0c9ea79f9bace118c8200aa004ba90b2a00000068007400740070003a002f002f007700770077002e00690063006f00640065002e007200 +6f002f000000}}}{\fldrslt {\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 http://www.icode.ro/}}}{\f39\fs20\insrsid7485074\charrsid7485074 ) +\par }{\field{\*\fldinst {\f39\fs20\insrsid7485074 HYPERLINK "hardwiredteks@gmail.com" }{\f39\fs20\insrsid15940516\charrsid7485074 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c00000000000004600001800000068617264776972656474656b7340676d61696c2e636f6d00ffffadde000000000000000000000000000000000000000000000000}}}{\fldrslt { +\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 hardwiredteks@gmail.com}}}{\f39\fs20\insrsid7485074\charrsid7485074 , }{\field{\*\fldinst {\f39\fs20\insrsid7485074 HYPERLINK "hardwired@icode.ro" }{\f39\fs20\insrsid15940516\charrsid7485074 +{\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c0000000000000460000130000006861726477697265644069636f64652e726f00ffffadde000000000000000000000000000000000000000000000000}}}{\fldrslt { +\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 hardwired@icode.ro}}}{\f39\fs20\insrsid7485074\charrsid7485074 +\par }} \ No newline at end of file diff --git a/admin/win/nsi/nsis_processes/readme.txt b/admin/win/nsi/nsis_processes/readme.txt new file mode 100755 index 000000000..8529c39ad --- /dev/null +++ b/admin/win/nsi/nsis_processes/readme.txt @@ -0,0 +1,122 @@ +---------------------------------------------------------------- +---------------------------------------------------------------- +Processes (Processes.dll) +Version: 1.0.1.0 +Release: 24.february.2005 +Description: Nullsoft Installer (NSIS) plug-in for managing?! + Windows processes. + +Copyright: © 2004-2005 Hardwired. No rights reserved. + There is no restriction and no guaranty for using + this software. + +Author: Andrei Ciubotaru [Hardwired] + Lead Developer ICode&Ideas SRL (http://www.icode.ro/) + hardwiredteks@gmail.com, hardwired@icode.ro + +---------------------------------------------------------------- +---------------------------------------------------------------- +INTRODUCTION + + The Need For Plug-in - I need it for the one of my installers. + + Briefly: Use it when you need to find\kill a process when +installing\uninstalling some application. Also, use it when you +need to test the presence of a device driver. + + +SUPPORT + + Supported platforms are: WinNT,Win2K,WinXP and Win2003 Server. + + +DESCRIPTION + + Processes::FindProcess ;without ".exe" + + Searches the currently running processes for the given + process name. + + return: 1 - the process was found + 0 - the process was not found + + Processes::KillProcess ; without ".exe" + + Searches the currently running processes for the given + process name. If the process is found then the it gets + killed. + + return: 1 - the process was found and killed + 0 - the process was not found or the process + cannot be killed (insuficient rights) + + Processes::FindDevice + + Searches the installed devices drivers for the given + device base name. + (important: I said BASE NAME not FILENAME) + + return: 1 - the device driver was found + 0 - the device driver was not found + + +USAGE + + First of all, does not matter where you use it. Ofcourse, the +routines must be called inside of a Section/Function scope. + + Processes::FindProcess "process_name" + Pop $R0 + + StrCmp $R0 "1" make_my_day noooooo + + make_my_day: + ... + + noooooo: + ... + + + Processes::KillProcess "process_name" + Pop $R0 + + StrCmp $R0 "1" dead_meat why_wont_you_die + + dead_meat: + ... + + why_wont_you_die: + ... + + + Processes::FindDevice "device_base_name" + Pop $R0 + + StrCmp $R0 "1" blabla more_blabla + + blabla: + ... + + more_blabla: + ... + + +THANKS + + Sunil Kamath for inspiring me. I wanted to use its FindProcDLL +but my requirements made it imposible. + + Nullsoft for creating this very powerfull installer. One big, +free and full-featured (hmmm... and guiless for the moment) mean +install machine!:) + + ME for being such a great coder... + ... HAHAHAHAHAHAHA! + +ONE MORE THING + + If you use the plugin or it's source-code, I would apreciate +if my name is mentioned. + +---------------------------------------------------------------- +---------------------------------------------------------------- diff --git a/admin/win/nsi/nsis_processes/src/StdAfx.cpp b/admin/win/nsi/nsis_processes/src/StdAfx.cpp new file mode 100755 index 000000000..f38accc8a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/StdAfx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// KillProcDLL.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/admin/win/nsi/nsis_processes/src/StdAfx.h b/admin/win/nsi/nsis_processes/src/StdAfx.h new file mode 100755 index 000000000..dd49f99b9 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/StdAfx.h @@ -0,0 +1,34 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_) +#define AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include + +#include // String management... + +//From exam28.cpp +#include +//#include + +#ifdef BORLANDC + #include + #include +#endif + +//To make it a NSIS Plug-In +#include "exdll.h" + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_) diff --git a/admin/win/nsi/nsis_processes/src/exdll.c b/admin/win/nsi/nsis_processes/src/exdll.c new file mode 100755 index 000000000..7092cb840 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/exdll.c @@ -0,0 +1,37 @@ +#include +#include "exdll.h" + +HINSTANCE g_hInstance; + +HWND g_hwndParent; + +void __declspec(dllexport) myFunction(HWND hwndParent, int string_size, + char *variables, stack_t **stacktop) +{ + g_hwndParent=hwndParent; + + EXDLL_INIT(); + + + // note if you want parameters from the stack, pop them off in order. + // i.e. if you are called via exdll::myFunction file.dat poop.dat + // calling popstring() the first time would give you file.dat, + // and the second time would give you poop.dat. + // you should empty the stack of your parameters, and ONLY your + // parameters. + + // do your stuff here + { + char buf[1024]; + wsprintf(buf,"$0=%s\n",getuservariable(INST_0)); + MessageBox(g_hwndParent,buf,0,MB_OK); + } +} + + + +BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + g_hInstance=hInst; + return TRUE; +} diff --git a/admin/win/nsi/nsis_processes/src/exdll.h b/admin/win/nsi/nsis_processes/src/exdll.h new file mode 100755 index 000000000..777d93be5 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/exdll.h @@ -0,0 +1,136 @@ +#ifndef _EXDLL_H_ +#define _EXDLL_H_ + + + + + +// +// only include this file from one place in your DLL. +// (it is all static, if you use it in two places it will fail) +// +#define EXDLL_INIT() { \ + g_stringsize = string_size; \ + g_stacktop = stacktop; \ + g_variables = variables; } + + + + +// +// For page showing plug-ins +// +#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8) +#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd) +#define NOTIFY_BYE_BYE 'x' + +typedef struct _stack_t +{ + struct _stack_t *next; + char text[1]; // this should be the length of string_size +} stack_t; + + +static unsigned int g_stringsize; +static stack_t **g_stacktop; +static char *g_variables; + +enum +{ +INST_0, // $0 +INST_1, // $1 +INST_2, // $2 +INST_3, // $3 +INST_4, // $4 +INST_5, // $5 +INST_6, // $6 +INST_7, // $7 +INST_8, // $8 +INST_9, // $9 +INST_R0, // $R0 +INST_R1, // $R1 +INST_R2, // $R2 +INST_R3, // $R3 +INST_R4, // $R4 +INST_R5, // $R5 +INST_R6, // $R6 +INST_R7, // $R7 +INST_R8, // $R8 +INST_R9, // $R9 +INST_CMDLINE, // $CMDLINE +INST_INSTDIR, // $INSTDIR +INST_OUTDIR, // $OUTDIR +INST_EXEDIR, // $EXEDIR +INST_LANG, // $LANGUAGE +__INST_LAST +}; + + + + + +// +// utility functions (not required but often useful) +// +static int popstring( char *str ) +{ + stack_t *th; + + + if( !g_stacktop || + !*g_stacktop ) + return 1; + + th = (*g_stacktop); + lstrcpy( str, th->text ); + *g_stacktop = th->next; + GlobalFree( (HGLOBAL)th ); + + return 0; +} + + + + +static void pushstring( char *str ) +{ + stack_t *th; + + + if( !g_stacktop ) + return; + + th = (stack_t*)GlobalAlloc( GPTR, sizeof(stack_t) + g_stringsize ); + lstrcpyn( th->text, str, g_stringsize ); + th->next = *g_stacktop; + *g_stacktop = th; +} + + + + + +static char *getuservariable( int varnum ) +{ + if( varnum < 0 || + varnum >= __INST_LAST ) + return NULL; + + return (g_variables + varnum*g_stringsize); +} + + + + + +static void setuservariable( int varnum, char *var ) +{ + if( var != NULL && + varnum >= 0 && + varnum < __INST_LAST ) + lstrcpy( g_variables + varnum*g_stringsize, var ); +} + + + +#endif//_EXDLL_H_ \ No newline at end of file diff --git a/admin/win/nsi/nsis_processes/src/processes.cpp b/admin/win/nsi/nsis_processes/src/processes.cpp new file mode 100755 index 000000000..c15f8f94a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.cpp @@ -0,0 +1,411 @@ +#include "stdafx.h" +#include "processes.h" +#include "string.h" + + + + + + +//------------------------------------------------------------------------------------------- +// global variables +lpfEnumProcesses EnumProcesses; +lpfEnumProcessModules EnumProcessModules; +lpfGetModuleBaseName GetModuleBaseName; +lpfEnumDeviceDrivers EnumDeviceDrivers; +lpfGetDeviceDriverBaseName GetDeviceDriverBaseName; + +HINSTANCE g_hInstance; +HWND g_hwndParent; +HINSTANCE g_hInstLib; + + + + + +//------------------------------------------------------------------------------------------- +// main DLL entry +BOOL WINAPI _DllMainCRTStartup( HANDLE hInst, + ULONG ul_reason_for_call, + LPVOID lpReserved ) +{ + g_hInstance = (struct HINSTANCE__ *)hInst; + + return TRUE; +} + + + + + +//------------------------------------------------------------------------------------------- +// loads the psapi routines +bool LoadPSAPIRoutines( void ) +{ + if( NULL == (g_hInstLib = LoadLibraryA( "PSAPI.DLL" )) ) + return false; + + EnumProcesses = (lpfEnumProcesses) GetProcAddress( g_hInstLib, "EnumProcesses" ); + EnumProcessModules = (lpfEnumProcessModules) GetProcAddress( g_hInstLib, "EnumProcessModules" ); + GetModuleBaseName = (lpfGetModuleBaseName) GetProcAddress( g_hInstLib, "GetModuleBaseNameA" ); + EnumDeviceDrivers = (lpfEnumDeviceDrivers) GetProcAddress( g_hInstLib, "EnumDeviceDrivers" ); + GetDeviceDriverBaseName = (lpfGetDeviceDriverBaseName) GetProcAddress( g_hInstLib, "GetDeviceDriverBaseNameA" ); + + if( ( NULL == EnumProcesses ) || + ( NULL == EnumProcessModules ) || + ( NULL == EnumDeviceDrivers ) || + ( NULL == GetModuleBaseName ) || + ( NULL == GetDeviceDriverBaseName ) ) + { + FreeLibrary( g_hInstLib ); + + return false; + } + + return true; +} + + + + + +//------------------------------------------------------------------------------------------- +// free the psapi routines +bool FreePSAPIRoutines( void ) +{ + EnumProcesses = NULL; + EnumProcessModules = NULL; + GetModuleBaseName = NULL; + EnumDeviceDrivers = NULL; + + if( FALSE == FreeLibrary( g_hInstLib ) ) + return false; + + return true; +} + + + + + +//------------------------------------------------------------------------------------------- +// find a process by name +// return value: true - process was found +// false - process not found +bool FindProc( char *szProcess ) +{ + char szProcessName[ 1024 ]; + char szCurrentProcessName[ 1024 ]; + DWORD dPID[ 1024 ]; + DWORD dPIDSize( 1024 ); + DWORD dSize( 1024 ); + HANDLE hProcess; + HMODULE phModule[ 1024 ]; + + + // + // make the name lower case + // + memset( szProcessName, 0, 1024*sizeof(char) ); + sprintf( szProcessName, "%s", szProcess ); + strlwr( szProcessName ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate processes names + // + if( FALSE == EnumProcesses( dPID, dSize, &dPIDSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the process is running + // + for( int k( dPIDSize / sizeof( DWORD ) ); k >= 0; k-- ) + { + memset( szCurrentProcessName, 0, 1024*sizeof(char) ); + + if( NULL != ( hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dPID[ k ] ) ) ) + { + if( TRUE == EnumProcessModules( hProcess, phModule, sizeof(HMODULE)*1024, &dPIDSize ) ) + if( GetModuleBaseName( hProcess, phModule[ 0 ], szCurrentProcessName, 1024 ) > 0 ) + { + strlwr( szCurrentProcessName ); + + if( NULL != strstr( szCurrentProcessName, szProcessName ) ) + { + FreePSAPIRoutines(); + CloseHandle( hProcess ); + + return true; + } + } + + CloseHandle( hProcess ); + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +// kills a process by name +// return value: true - process was found +// false - process not found +bool KillProc( char *szProcess ) +{ + char szProcessName[ 1024 ]; + char szCurrentProcessName[ 1024 ]; + DWORD dPID[ 1024 ]; + DWORD dPIDSize( 1024 ); + DWORD dSize( 1024 ); + HANDLE hProcess; + HMODULE phModule[ 1024 ]; + + + // + // make the name lower case + // + memset( szProcessName, 0, 1024*sizeof(char) ); + sprintf( szProcessName, "%s", szProcess ); + strlwr( szProcessName ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate processes names + // + if( FALSE == EnumProcesses( dPID, dSize, &dPIDSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the process is running + // + for( int k( dPIDSize / sizeof( DWORD ) ); k >= 0; k-- ) + { + memset( szCurrentProcessName, 0, 1024*sizeof(char) ); + + if( NULL != ( hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dPID[ k ] ) ) ) + { + if( TRUE == EnumProcessModules( hProcess, phModule, sizeof(HMODULE)*1024, &dPIDSize ) ) + if( GetModuleBaseName( hProcess, phModule[ 0 ], szCurrentProcessName, 1024 ) > 0 ) + { + strlwr( szCurrentProcessName ); + + if( NULL != strstr( szCurrentProcessName, szProcessName ) ) + { + FreePSAPIRoutines(); + + // + // kill process + // + if( false == TerminateProcess( hProcess, 0 ) ) + { + CloseHandle( hProcess ); + + return true; + } + + // + // refresh systray + // + UpdateWindow( FindWindow( NULL, "Shell_TrayWnd" ) ); + + // + // refresh desktop window + // + UpdateWindow( GetDesktopWindow() ); + + CloseHandle( hProcess ); + + return true; + } + } + + CloseHandle( hProcess ); + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +bool FindDev( char *szDriverName ) +{ + char szDeviceName[ 1024 ]; + char szCurrentDeviceName[ 1024 ]; + LPVOID lpDevices[ 1024 ]; + DWORD dDevicesSize( 1024 ); + DWORD dSize( 1024 ); + TCHAR tszCurrentDeviceName[ 1024 ]; + DWORD dNameSize( 1024 ); + + + // + // make the name lower case + // + memset( szDeviceName, 0, 1024*sizeof(char) ); + sprintf( szDeviceName, "%s", strlwr( szDriverName ) ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate devices + // + if( FALSE == EnumDeviceDrivers( lpDevices, dSize, &dDevicesSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the device driver exists + // + for( int k( dDevicesSize / sizeof( LPVOID ) ); k >= 0; k-- ) + { + memset( szCurrentDeviceName, 0, 1024*sizeof(char) ); + memset( tszCurrentDeviceName, 0, 1024*sizeof(TCHAR) ); + + if( 0 != GetDeviceDriverBaseName( lpDevices[ k ], tszCurrentDeviceName, dNameSize ) ) + { + sprintf( szCurrentDeviceName, "%S", tszCurrentDeviceName ); + + if( 0 == strcmp( strlwr( szCurrentDeviceName ), szDeviceName ) ) + { + FreePSAPIRoutines(); + + return true; + } + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void FindProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == FindProc( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void KillProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == KillProc( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void FindDevice( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == FindDev( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} diff --git a/admin/win/nsi/nsis_processes/src/processes.h b/admin/win/nsi/nsis_processes/src/processes.h new file mode 100755 index 000000000..9bd069101 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.h @@ -0,0 +1,49 @@ +#pragma once + + + + + +//------------------------------------------------------------------------------------------- +// PSAPI function pointers +typedef BOOL (WINAPI *lpfEnumProcesses) ( DWORD *, DWORD, DWORD * ); +typedef BOOL (WINAPI *lpfEnumProcessModules) ( HANDLE, HMODULE *, DWORD, LPDWORD ); +typedef DWORD (WINAPI *lpfGetModuleBaseName) ( HANDLE, HMODULE, LPTSTR, DWORD ); +typedef BOOL (WINAPI *lpfEnumDeviceDrivers) ( LPVOID *, DWORD, LPDWORD ); +typedef BOOL (WINAPI *lpfGetDeviceDriverBaseName)( LPVOID, LPTSTR, DWORD ); + + + + + + +//------------------------------------------------------------------------------------------- +// Internal use routines +bool LoadPSAPIRoutines( void ); +bool FreePSAPIRoutines( void ); + +bool FindProc( char *szProcess ); +bool KillProc( char *szProcess ); + +bool FindDev( char *szDriverName ); + + + + + +//------------------------------------------------------------------------------------------- +// Exported routines +extern "C" __declspec(dllexport) void FindProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); + +extern "C" __declspec(dllexport) void KillProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); + +extern "C" __declspec(dllexport) void FindDevice( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); diff --git a/admin/win/nsi/nsis_processes/src/processes.ncb b/admin/win/nsi/nsis_processes/src/processes.ncb new file mode 100755 index 0000000000000000000000000000000000000000..c1a5f281fe57fcc5570c40953586a2799befc159 GIT binary patch literal 44032 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 literal 0 HcmV?d00001 diff --git a/admin/win/nsi/nsis_processes/src/processes.rc b/admin/win/nsi/nsis_processes/src/processes.rc new file mode 100755 index 000000000..c6e62a3c8 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.rc @@ -0,0 +1,103 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "NSIS Plug-in for Windows process management. Only WinNT, Win2K, WinXP and Win2003 Server supported." + VALUE "CompanyName", "Andrei Ciubotaru [Hardwired]" + VALUE "FileDescription", "Windows Processes Management" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "Processes" + VALUE "LegalCopyright", "Copyright (c) 2004 Hardwired. No rights reserved." + VALUE "OriginalFilename", "Processes.dll" + VALUE "ProductName", "Processes" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/admin/win/nsi/nsis_processes/src/processes.sln b/admin/win/nsi/nsis_processes/src/processes.sln new file mode 100755 index 000000000..73fc989e2 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "processes", "processes.vcproj", "{3438467F-A719-46DC-93E5-137A8B691727}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {3438467F-A719-46DC-93E5-137A8B691727}.Debug.ActiveCfg = Debug|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Debug.Build.0 = Debug|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Release.ActiveCfg = Release|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/admin/win/nsi/nsis_processes/src/processes.txt b/admin/win/nsi/nsis_processes/src/processes.txt new file mode 100755 index 000000000..51d11902a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.txt @@ -0,0 +1,122 @@ +---------------------------------------------------------------- +---------------------------------------------------------------- +Processes (Processes.dll) +Version: 1.0.0.1 +Release: 12.december.2004 +Description:Nullsoft Installer (NSIS) plug-in for managing?! + Windows processes. + +Copyright: © 2004 Hardwired. No rights reserved. + There is no restriction and no guaranty for using + this software. + +Author: Andrei Ciubotaru [Hardwired] + Lead Developer ICode&Ideas SRL (http://www.icode.ro) + hardwiredteks@gmail.com, hardwired@icode.ro + +---------------------------------------------------------------- +---------------------------------------------------------------- +INTRODUCTION + + The Need For Plug-in - I need it for the one of my installers. + + Briefly: Use it when you need to find\kill a process when +installing\uninstalling some application. Also, use it when you +need to test the presence of a device driver. + + +SUPPORT + + Supported platforms are: WinNT,Win2K,WinXP and Win2003 Server. + + +DESCRIPTION + + Processes::FindProcess + + Searches the currently running processes for the given + process name. + + return: 1 - the process was found + 0 - the process was not found + + Processes::KillProcess + + Searches the currently running processes for the given + process name. If the process is found then the it gets + killed. + + return: 1 - the process was found and killed + 0 - the process was not found or the process + cannot be killed (insuficient rights) + + Processes::FindDevice + + Searches the installed devices drivers for the given + device base name. + (important: I said BASE NAME not FILENAME) + + return: 1 - the device driver was found + 0 - the device driver was not found + + +USAGE + + First of all, does not matter where you use it. Ofcourse, the +routines must be called inside of a Section/Function scope. + + Processes::FindProcess "process_name.exe" + Pop $R0 + + StrCmp $R0 "1" make_my_day noooooo + + make_my_day: + ... + + noooooo: + ... + + + Processes::KillProcess "process_name.exe" + Pop $R0 + + StrCmp $R0 "1" dead_meat why_wont_you_die + + dead_meat: + ... + + why_wont_you_die: + ... + + + Processes::FindDevice "device_base_name" + Pop $R0 + + StrCmp $R0 "1" blabla more_blabla + + blabla: + ... + + more_blabla: + ... + + +THANKS + + Sunil Kamath for inspiring me. I wanted to use its FindProcDLL +but my requirements made it imposible. + + Nullsoft for creating this very powerfull installer. One big, +free and full-featured (hmmm... and guiless for the moment) mean +install machine!:) + + ME for being such a great coder... + ... HAHAHAHAHAHAHA! + +ONE MORE THING + + If you use the plugin or it's source-code, I would apreciate +if my name is mentioned. + +---------------------------------------------------------------- +---------------------------------------------------------------- diff --git a/admin/win/nsi/nsis_processes/src/processes.vcproj b/admin/win/nsi/nsis_processes/src/processes.vcproj new file mode 100755 index 000000000..245cbc99f --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.vcproj @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/admin/win/nsi/nsis_processes/src/resource.h b/admin/win/nsi/nsis_processes/src/resource.h new file mode 100755 index 000000000..506377e21 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by processes.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/admin/win/nsi/page_header.bmp b/admin/win/nsi/page_header.bmp old mode 100755 new mode 100644 diff --git a/admin/win/nsi/revision.txt b/admin/win/nsi/revision.txt old mode 100644 new mode 100755 index 56749c830..c4fbb1cfa --- a/admin/win/nsi/revision.txt +++ b/admin/win/nsi/revision.txt @@ -1 +1 @@ -96 \ No newline at end of file +97 \ No newline at end of file diff --git a/admin/win/nsi/tomahawk.ini b/admin/win/nsi/tomahawk.ini old mode 100755 new mode 100644 diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi old mode 100755 new mode 100644 index f6e49cfd3..c2d2a9bd1 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -159,6 +159,45 @@ Function LaunchTomahawkAsUser FunctionEnd !endif +############################################################################## +# # +# PROCESS HANDLING FUNCTIONS AND MACROS # +# # +############################################################################## + +!macro CheckForProcess processName gotoWhenFound gotoWhenNotFound + Processes::FindProcess ${processName} + StrCmp $R0 "0" ${gotoWhenNotFound} ${gotoWhenFound} +!macroend + +!macro ConfirmEndProcess processName + MessageBox MB_YESNO|MB_ICONEXCLAMATION \ + "Found ${processName} process(s) which need to be stopped.$\nDo you want the installer to stop these for you?" \ + IDYES process_${processName}_kill IDNO process_${processName}_ended + process_${processName}_kill: + DetailPrint "Killing ${processName} processes." + Processes::KillProcess ${processName} + Sleep 1500 + StrCmp $R0 "1" process_${processName}_ended + DetailPrint "Process to kill not found!" + process_${processName}_ended: +!macroend + +!macro CheckAndConfirmEndProcess processName + !insertmacro CheckForProcess ${processName} 0 no_process_${processName}_to_end + !insertmacro ConfirmEndProcess ${processName} + no_process_${processName}_to_end: +!macroend + +!macro EnsureTomahawkShutdown un + Function ${un}EnsureTomahawkShutdown + !insertmacro CheckAndConfirmEndProcess "tomahawk.exe" + FunctionEnd +!macroend + +!insertmacro EnsureTomahawkShutdown "" +!insertmacro EnsureTomahawkShutdown "un." + ############################################################################## # # # RE-INSTALLER FUNCTIONS # @@ -561,6 +600,9 @@ Function .onInit StrCmp $R0 "" SkipSetInstDir StrCpy $INSTDIR $R0 SkipSetInstDir: + + ;Shutdown Tomahawk in case Add/Remove re-installer option used. + Call EnsureTomahawkShutdown FunctionEnd Function .onInstSuccess diff --git a/admin/win/nsi/welcome.bmp b/admin/win/nsi/welcome.bmp old mode 100755 new mode 100644 diff --git a/admin/win/page_header.bmp b/admin/win/page_header.bmp deleted file mode 100755 index c50025a22b810a7e755768c16e73ce7f1ec97ea1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25818 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 KfYm_fH1L1OIOjJ2 diff --git a/admin/win/welcome.bmp b/admin/win/welcome.bmp deleted file mode 100755 index 580504f50354f345e789f921266ebcdd518037ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154542 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 From 543c3125accbbedbf9adf5897d27d7e9c90a40ff Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 31 Mar 2011 04:20:57 +0200 Subject: [PATCH 32/63] * Cherry-picked latest tomahawk.nsi changes from master. --- admin/win/README.txt | 0 admin/win/nsi/RELEASE_NOTES.txt | 0 admin/win/nsi/installer.ico | Bin .../win/nsi/nsis_processes/bin/Processes.dll | Bin 0 -> 36352 bytes admin/win/nsi/nsis_processes/license.rtf | 35 ++ admin/win/nsi/nsis_processes/readme.txt | 122 ++++++ admin/win/nsi/nsis_processes/src/StdAfx.cpp | 8 + admin/win/nsi/nsis_processes/src/StdAfx.h | 34 ++ admin/win/nsi/nsis_processes/src/exdll.c | 37 ++ admin/win/nsi/nsis_processes/src/exdll.h | 136 ++++++ .../win/nsi/nsis_processes/src/processes.cpp | 411 ++++++++++++++++++ admin/win/nsi/nsis_processes/src/processes.h | 49 +++ .../win/nsi/nsis_processes/src/processes.ncb | Bin 0 -> 44032 bytes admin/win/nsi/nsis_processes/src/processes.rc | 103 +++++ .../win/nsi/nsis_processes/src/processes.sln | 21 + .../win/nsi/nsis_processes/src/processes.txt | 122 ++++++ .../nsi/nsis_processes/src/processes.vcproj | 222 ++++++++++ admin/win/nsi/nsis_processes/src/resource.h | 15 + admin/win/nsi/page_header.bmp | Bin admin/win/nsi/revision.txt | 2 +- admin/win/nsi/tomahawk.ini | 0 admin/win/nsi/tomahawk.nsi | 49 +++ admin/win/nsi/welcome.bmp | Bin 23 files changed, 1365 insertions(+), 1 deletion(-) delete mode 100755 admin/win/README.txt mode change 100755 => 100644 admin/win/nsi/RELEASE_NOTES.txt mode change 100755 => 100644 admin/win/nsi/installer.ico create mode 100755 admin/win/nsi/nsis_processes/bin/Processes.dll create mode 100755 admin/win/nsi/nsis_processes/license.rtf create mode 100755 admin/win/nsi/nsis_processes/readme.txt create mode 100755 admin/win/nsi/nsis_processes/src/StdAfx.cpp create mode 100755 admin/win/nsi/nsis_processes/src/StdAfx.h create mode 100755 admin/win/nsi/nsis_processes/src/exdll.c create mode 100755 admin/win/nsi/nsis_processes/src/exdll.h create mode 100755 admin/win/nsi/nsis_processes/src/processes.cpp create mode 100755 admin/win/nsi/nsis_processes/src/processes.h create mode 100755 admin/win/nsi/nsis_processes/src/processes.ncb create mode 100755 admin/win/nsi/nsis_processes/src/processes.rc create mode 100755 admin/win/nsi/nsis_processes/src/processes.sln create mode 100755 admin/win/nsi/nsis_processes/src/processes.txt create mode 100755 admin/win/nsi/nsis_processes/src/processes.vcproj create mode 100755 admin/win/nsi/nsis_processes/src/resource.h mode change 100755 => 100644 admin/win/nsi/page_header.bmp mode change 100644 => 100755 admin/win/nsi/revision.txt mode change 100755 => 100644 admin/win/nsi/tomahawk.ini mode change 100755 => 100644 admin/win/nsi/tomahawk.nsi mode change 100755 => 100644 admin/win/nsi/welcome.bmp diff --git a/admin/win/README.txt b/admin/win/README.txt deleted file mode 100755 index e69de29bb..000000000 diff --git a/admin/win/nsi/RELEASE_NOTES.txt b/admin/win/nsi/RELEASE_NOTES.txt old mode 100755 new mode 100644 diff --git a/admin/win/nsi/installer.ico b/admin/win/nsi/installer.ico old mode 100755 new mode 100644 diff --git a/admin/win/nsi/nsis_processes/bin/Processes.dll b/admin/win/nsi/nsis_processes/bin/Processes.dll new file mode 100755 index 0000000000000000000000000000000000000000..e532bf8bb55a5b7cadfe6d6e05058ae3f446739a GIT binary patch literal 36352 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≦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 literal 0 HcmV?d00001 diff --git a/admin/win/nsi/nsis_processes/license.rtf b/admin/win/nsi/nsis_processes/license.rtf new file mode 100755 index 000000000..2ce5a58c9 --- /dev/null +++ b/admin/win/nsi/nsis_processes/license.rtf @@ -0,0 +1,35 @@ +{\rtf1\ansi\ansicpg1252\uc1\deff0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f39\fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Verdana;} +{\f172\froman\fcharset238\fprq2 Times New Roman CE;}{\f173\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f175\froman\fcharset161\fprq2 Times New Roman Greek;}{\f176\froman\fcharset162\fprq2 Times New Roman Tur;} +{\f177\froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f178\froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f179\froman\fcharset186\fprq2 Times New Roman Baltic;}{\f180\froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\f562\fswiss\fcharset238\fprq2 Verdana CE;}{\f563\fswiss\fcharset204\fprq2 Verdana Cyr;}{\f565\fswiss\fcharset161\fprq2 Verdana Greek;}{\f566\fswiss\fcharset162\fprq2 Verdana Tur;}{\f569\fswiss\fcharset186\fprq2 Verdana Baltic;} +{\f570\fswiss\fcharset163\fprq2 Verdana (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255; +\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\stylesheet{ +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 Normal;}{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \snext11 \ssemihidden Normal Table;}{\*\cs15 \additive \ul\cf2 \sbasedon10 \styrsid7485074 Hyperlink;}} +{\*\latentstyles\lsdstimax156\lsdlockeddef0}{\*\rsidtbl \rsid6712196\rsid7485074\rsid11352300\rsid15940516}{\*\generator Microsoft Word 11.0.5604;}{\info{\title Processes v1}{\author Hardwired}{\operator Hardwired}{\creatim\yr2004\mo12\dy12\hr23\min42} +{\revtim\yr2004\mo12\dy12\hr23\min51}{\version2}{\edmins9}{\nofpages1}{\nofwords80}{\nofchars458}{\nofcharsws537}{\vern24689}}\widowctrl\ftnbj\aenddoc\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180 +\dgvspace180\dghorigin1800\dgvorigin1440\dghshow1\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct +\asianbrkrule\rsidroot7485074\newtblstyruls\nogrowautofit \fet0\sectd \linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}} +{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (} +{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain +\qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7485074 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\b\f39\insrsid7485074\charrsid7485074 Processes v1.0}{\f39\insrsid7485074\charrsid7485074 .0.1 +\par }{\f39\fs20\insrsid7485074 +\par }\pard \qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid15940516 {\f39\fs20\insrsid15940516 This software binaries and source-code are free for any kind of use, including commercial use. }{ +\f39\fs20\insrsid7485074\charrsid7485074 There is no restriction and no guaranty for using}{\f39\fs20\insrsid7485074\charrsid7485074 t}{\f39\fs20\insrsid7485074\charrsid7485074 his software}{\f39\fs20\insrsid7485074\charrsid7485074 and/or it +s source-code. }{\f39\fs20\insrsid15940516 +\par I}{\f39\fs20\insrsid7485074\charrsid7485074 f you use the plug}{\f39\fs20\insrsid7485074\charrsid7485074 -}{\f39\fs20\insrsid7485074\charrsid7485074 in }{\f39\fs20\insrsid7485074\charrsid7485074 and/}{\f39\fs20\insrsid7485074\charrsid7485074 or it}{ +\f39\fs20\insrsid7485074\charrsid7485074 s}{\f39\fs20\insrsid7485074\charrsid7485074 source-code, I would }{\f39\fs20\insrsid7485074\charrsid7485074 appreciate }{\f39\fs20\insrsid7485074\charrsid7485074 if my name is mentioned.}{ +\f39\fs20\insrsid7485074\charrsid7485074 +\par }\pard \qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7485074 {\f39\fs20\insrsid7485074\charrsid7485074 +\par }{\b\f39\fs20\insrsid7485074\charrsid7485074 Andrei Ciubotaru [Hardwired] +\par }{\f39\fs20\insrsid7485074\charrsid7485074 Lead Developer ICode&Ideas SRL (}{\field\flddirty{\*\fldinst {\f39\fs20\insrsid7485074\charrsid7485074 HYPERLINK "http://www.icode.ro/" }{\f39\fs20\insrsid7485074\charrsid7485074 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b02000000170000001500000068007400740070003a002f002f007700770077002e00690063006f00640065002e0072006f002f000000e0c9ea79f9bace118c8200aa004ba90b2a00000068007400740070003a002f002f007700770077002e00690063006f00640065002e007200 +6f002f000000}}}{\fldrslt {\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 http://www.icode.ro/}}}{\f39\fs20\insrsid7485074\charrsid7485074 ) +\par }{\field{\*\fldinst {\f39\fs20\insrsid7485074 HYPERLINK "hardwiredteks@gmail.com" }{\f39\fs20\insrsid15940516\charrsid7485074 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c00000000000004600001800000068617264776972656474656b7340676d61696c2e636f6d00ffffadde000000000000000000000000000000000000000000000000}}}{\fldrslt { +\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 hardwiredteks@gmail.com}}}{\f39\fs20\insrsid7485074\charrsid7485074 , }{\field{\*\fldinst {\f39\fs20\insrsid7485074 HYPERLINK "hardwired@icode.ro" }{\f39\fs20\insrsid15940516\charrsid7485074 +{\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c0000000000000460000130000006861726477697265644069636f64652e726f00ffffadde000000000000000000000000000000000000000000000000}}}{\fldrslt { +\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 hardwired@icode.ro}}}{\f39\fs20\insrsid7485074\charrsid7485074 +\par }} \ No newline at end of file diff --git a/admin/win/nsi/nsis_processes/readme.txt b/admin/win/nsi/nsis_processes/readme.txt new file mode 100755 index 000000000..8529c39ad --- /dev/null +++ b/admin/win/nsi/nsis_processes/readme.txt @@ -0,0 +1,122 @@ +---------------------------------------------------------------- +---------------------------------------------------------------- +Processes (Processes.dll) +Version: 1.0.1.0 +Release: 24.february.2005 +Description: Nullsoft Installer (NSIS) plug-in for managing?! + Windows processes. + +Copyright: © 2004-2005 Hardwired. No rights reserved. + There is no restriction and no guaranty for using + this software. + +Author: Andrei Ciubotaru [Hardwired] + Lead Developer ICode&Ideas SRL (http://www.icode.ro/) + hardwiredteks@gmail.com, hardwired@icode.ro + +---------------------------------------------------------------- +---------------------------------------------------------------- +INTRODUCTION + + The Need For Plug-in - I need it for the one of my installers. + + Briefly: Use it when you need to find\kill a process when +installing\uninstalling some application. Also, use it when you +need to test the presence of a device driver. + + +SUPPORT + + Supported platforms are: WinNT,Win2K,WinXP and Win2003 Server. + + +DESCRIPTION + + Processes::FindProcess ;without ".exe" + + Searches the currently running processes for the given + process name. + + return: 1 - the process was found + 0 - the process was not found + + Processes::KillProcess ; without ".exe" + + Searches the currently running processes for the given + process name. If the process is found then the it gets + killed. + + return: 1 - the process was found and killed + 0 - the process was not found or the process + cannot be killed (insuficient rights) + + Processes::FindDevice + + Searches the installed devices drivers for the given + device base name. + (important: I said BASE NAME not FILENAME) + + return: 1 - the device driver was found + 0 - the device driver was not found + + +USAGE + + First of all, does not matter where you use it. Ofcourse, the +routines must be called inside of a Section/Function scope. + + Processes::FindProcess "process_name" + Pop $R0 + + StrCmp $R0 "1" make_my_day noooooo + + make_my_day: + ... + + noooooo: + ... + + + Processes::KillProcess "process_name" + Pop $R0 + + StrCmp $R0 "1" dead_meat why_wont_you_die + + dead_meat: + ... + + why_wont_you_die: + ... + + + Processes::FindDevice "device_base_name" + Pop $R0 + + StrCmp $R0 "1" blabla more_blabla + + blabla: + ... + + more_blabla: + ... + + +THANKS + + Sunil Kamath for inspiring me. I wanted to use its FindProcDLL +but my requirements made it imposible. + + Nullsoft for creating this very powerfull installer. One big, +free and full-featured (hmmm... and guiless for the moment) mean +install machine!:) + + ME for being such a great coder... + ... HAHAHAHAHAHAHA! + +ONE MORE THING + + If you use the plugin or it's source-code, I would apreciate +if my name is mentioned. + +---------------------------------------------------------------- +---------------------------------------------------------------- diff --git a/admin/win/nsi/nsis_processes/src/StdAfx.cpp b/admin/win/nsi/nsis_processes/src/StdAfx.cpp new file mode 100755 index 000000000..f38accc8a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/StdAfx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// KillProcDLL.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/admin/win/nsi/nsis_processes/src/StdAfx.h b/admin/win/nsi/nsis_processes/src/StdAfx.h new file mode 100755 index 000000000..dd49f99b9 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/StdAfx.h @@ -0,0 +1,34 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_) +#define AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include + +#include // String management... + +//From exam28.cpp +#include +//#include + +#ifdef BORLANDC + #include + #include +#endif + +//To make it a NSIS Plug-In +#include "exdll.h" + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_) diff --git a/admin/win/nsi/nsis_processes/src/exdll.c b/admin/win/nsi/nsis_processes/src/exdll.c new file mode 100755 index 000000000..7092cb840 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/exdll.c @@ -0,0 +1,37 @@ +#include +#include "exdll.h" + +HINSTANCE g_hInstance; + +HWND g_hwndParent; + +void __declspec(dllexport) myFunction(HWND hwndParent, int string_size, + char *variables, stack_t **stacktop) +{ + g_hwndParent=hwndParent; + + EXDLL_INIT(); + + + // note if you want parameters from the stack, pop them off in order. + // i.e. if you are called via exdll::myFunction file.dat poop.dat + // calling popstring() the first time would give you file.dat, + // and the second time would give you poop.dat. + // you should empty the stack of your parameters, and ONLY your + // parameters. + + // do your stuff here + { + char buf[1024]; + wsprintf(buf,"$0=%s\n",getuservariable(INST_0)); + MessageBox(g_hwndParent,buf,0,MB_OK); + } +} + + + +BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + g_hInstance=hInst; + return TRUE; +} diff --git a/admin/win/nsi/nsis_processes/src/exdll.h b/admin/win/nsi/nsis_processes/src/exdll.h new file mode 100755 index 000000000..777d93be5 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/exdll.h @@ -0,0 +1,136 @@ +#ifndef _EXDLL_H_ +#define _EXDLL_H_ + + + + + +// +// only include this file from one place in your DLL. +// (it is all static, if you use it in two places it will fail) +// +#define EXDLL_INIT() { \ + g_stringsize = string_size; \ + g_stacktop = stacktop; \ + g_variables = variables; } + + + + +// +// For page showing plug-ins +// +#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8) +#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd) +#define NOTIFY_BYE_BYE 'x' + +typedef struct _stack_t +{ + struct _stack_t *next; + char text[1]; // this should be the length of string_size +} stack_t; + + +static unsigned int g_stringsize; +static stack_t **g_stacktop; +static char *g_variables; + +enum +{ +INST_0, // $0 +INST_1, // $1 +INST_2, // $2 +INST_3, // $3 +INST_4, // $4 +INST_5, // $5 +INST_6, // $6 +INST_7, // $7 +INST_8, // $8 +INST_9, // $9 +INST_R0, // $R0 +INST_R1, // $R1 +INST_R2, // $R2 +INST_R3, // $R3 +INST_R4, // $R4 +INST_R5, // $R5 +INST_R6, // $R6 +INST_R7, // $R7 +INST_R8, // $R8 +INST_R9, // $R9 +INST_CMDLINE, // $CMDLINE +INST_INSTDIR, // $INSTDIR +INST_OUTDIR, // $OUTDIR +INST_EXEDIR, // $EXEDIR +INST_LANG, // $LANGUAGE +__INST_LAST +}; + + + + + +// +// utility functions (not required but often useful) +// +static int popstring( char *str ) +{ + stack_t *th; + + + if( !g_stacktop || + !*g_stacktop ) + return 1; + + th = (*g_stacktop); + lstrcpy( str, th->text ); + *g_stacktop = th->next; + GlobalFree( (HGLOBAL)th ); + + return 0; +} + + + + +static void pushstring( char *str ) +{ + stack_t *th; + + + if( !g_stacktop ) + return; + + th = (stack_t*)GlobalAlloc( GPTR, sizeof(stack_t) + g_stringsize ); + lstrcpyn( th->text, str, g_stringsize ); + th->next = *g_stacktop; + *g_stacktop = th; +} + + + + + +static char *getuservariable( int varnum ) +{ + if( varnum < 0 || + varnum >= __INST_LAST ) + return NULL; + + return (g_variables + varnum*g_stringsize); +} + + + + + +static void setuservariable( int varnum, char *var ) +{ + if( var != NULL && + varnum >= 0 && + varnum < __INST_LAST ) + lstrcpy( g_variables + varnum*g_stringsize, var ); +} + + + +#endif//_EXDLL_H_ \ No newline at end of file diff --git a/admin/win/nsi/nsis_processes/src/processes.cpp b/admin/win/nsi/nsis_processes/src/processes.cpp new file mode 100755 index 000000000..c15f8f94a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.cpp @@ -0,0 +1,411 @@ +#include "stdafx.h" +#include "processes.h" +#include "string.h" + + + + + + +//------------------------------------------------------------------------------------------- +// global variables +lpfEnumProcesses EnumProcesses; +lpfEnumProcessModules EnumProcessModules; +lpfGetModuleBaseName GetModuleBaseName; +lpfEnumDeviceDrivers EnumDeviceDrivers; +lpfGetDeviceDriverBaseName GetDeviceDriverBaseName; + +HINSTANCE g_hInstance; +HWND g_hwndParent; +HINSTANCE g_hInstLib; + + + + + +//------------------------------------------------------------------------------------------- +// main DLL entry +BOOL WINAPI _DllMainCRTStartup( HANDLE hInst, + ULONG ul_reason_for_call, + LPVOID lpReserved ) +{ + g_hInstance = (struct HINSTANCE__ *)hInst; + + return TRUE; +} + + + + + +//------------------------------------------------------------------------------------------- +// loads the psapi routines +bool LoadPSAPIRoutines( void ) +{ + if( NULL == (g_hInstLib = LoadLibraryA( "PSAPI.DLL" )) ) + return false; + + EnumProcesses = (lpfEnumProcesses) GetProcAddress( g_hInstLib, "EnumProcesses" ); + EnumProcessModules = (lpfEnumProcessModules) GetProcAddress( g_hInstLib, "EnumProcessModules" ); + GetModuleBaseName = (lpfGetModuleBaseName) GetProcAddress( g_hInstLib, "GetModuleBaseNameA" ); + EnumDeviceDrivers = (lpfEnumDeviceDrivers) GetProcAddress( g_hInstLib, "EnumDeviceDrivers" ); + GetDeviceDriverBaseName = (lpfGetDeviceDriverBaseName) GetProcAddress( g_hInstLib, "GetDeviceDriverBaseNameA" ); + + if( ( NULL == EnumProcesses ) || + ( NULL == EnumProcessModules ) || + ( NULL == EnumDeviceDrivers ) || + ( NULL == GetModuleBaseName ) || + ( NULL == GetDeviceDriverBaseName ) ) + { + FreeLibrary( g_hInstLib ); + + return false; + } + + return true; +} + + + + + +//------------------------------------------------------------------------------------------- +// free the psapi routines +bool FreePSAPIRoutines( void ) +{ + EnumProcesses = NULL; + EnumProcessModules = NULL; + GetModuleBaseName = NULL; + EnumDeviceDrivers = NULL; + + if( FALSE == FreeLibrary( g_hInstLib ) ) + return false; + + return true; +} + + + + + +//------------------------------------------------------------------------------------------- +// find a process by name +// return value: true - process was found +// false - process not found +bool FindProc( char *szProcess ) +{ + char szProcessName[ 1024 ]; + char szCurrentProcessName[ 1024 ]; + DWORD dPID[ 1024 ]; + DWORD dPIDSize( 1024 ); + DWORD dSize( 1024 ); + HANDLE hProcess; + HMODULE phModule[ 1024 ]; + + + // + // make the name lower case + // + memset( szProcessName, 0, 1024*sizeof(char) ); + sprintf( szProcessName, "%s", szProcess ); + strlwr( szProcessName ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate processes names + // + if( FALSE == EnumProcesses( dPID, dSize, &dPIDSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the process is running + // + for( int k( dPIDSize / sizeof( DWORD ) ); k >= 0; k-- ) + { + memset( szCurrentProcessName, 0, 1024*sizeof(char) ); + + if( NULL != ( hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dPID[ k ] ) ) ) + { + if( TRUE == EnumProcessModules( hProcess, phModule, sizeof(HMODULE)*1024, &dPIDSize ) ) + if( GetModuleBaseName( hProcess, phModule[ 0 ], szCurrentProcessName, 1024 ) > 0 ) + { + strlwr( szCurrentProcessName ); + + if( NULL != strstr( szCurrentProcessName, szProcessName ) ) + { + FreePSAPIRoutines(); + CloseHandle( hProcess ); + + return true; + } + } + + CloseHandle( hProcess ); + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +// kills a process by name +// return value: true - process was found +// false - process not found +bool KillProc( char *szProcess ) +{ + char szProcessName[ 1024 ]; + char szCurrentProcessName[ 1024 ]; + DWORD dPID[ 1024 ]; + DWORD dPIDSize( 1024 ); + DWORD dSize( 1024 ); + HANDLE hProcess; + HMODULE phModule[ 1024 ]; + + + // + // make the name lower case + // + memset( szProcessName, 0, 1024*sizeof(char) ); + sprintf( szProcessName, "%s", szProcess ); + strlwr( szProcessName ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate processes names + // + if( FALSE == EnumProcesses( dPID, dSize, &dPIDSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the process is running + // + for( int k( dPIDSize / sizeof( DWORD ) ); k >= 0; k-- ) + { + memset( szCurrentProcessName, 0, 1024*sizeof(char) ); + + if( NULL != ( hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dPID[ k ] ) ) ) + { + if( TRUE == EnumProcessModules( hProcess, phModule, sizeof(HMODULE)*1024, &dPIDSize ) ) + if( GetModuleBaseName( hProcess, phModule[ 0 ], szCurrentProcessName, 1024 ) > 0 ) + { + strlwr( szCurrentProcessName ); + + if( NULL != strstr( szCurrentProcessName, szProcessName ) ) + { + FreePSAPIRoutines(); + + // + // kill process + // + if( false == TerminateProcess( hProcess, 0 ) ) + { + CloseHandle( hProcess ); + + return true; + } + + // + // refresh systray + // + UpdateWindow( FindWindow( NULL, "Shell_TrayWnd" ) ); + + // + // refresh desktop window + // + UpdateWindow( GetDesktopWindow() ); + + CloseHandle( hProcess ); + + return true; + } + } + + CloseHandle( hProcess ); + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +bool FindDev( char *szDriverName ) +{ + char szDeviceName[ 1024 ]; + char szCurrentDeviceName[ 1024 ]; + LPVOID lpDevices[ 1024 ]; + DWORD dDevicesSize( 1024 ); + DWORD dSize( 1024 ); + TCHAR tszCurrentDeviceName[ 1024 ]; + DWORD dNameSize( 1024 ); + + + // + // make the name lower case + // + memset( szDeviceName, 0, 1024*sizeof(char) ); + sprintf( szDeviceName, "%s", strlwr( szDriverName ) ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate devices + // + if( FALSE == EnumDeviceDrivers( lpDevices, dSize, &dDevicesSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the device driver exists + // + for( int k( dDevicesSize / sizeof( LPVOID ) ); k >= 0; k-- ) + { + memset( szCurrentDeviceName, 0, 1024*sizeof(char) ); + memset( tszCurrentDeviceName, 0, 1024*sizeof(TCHAR) ); + + if( 0 != GetDeviceDriverBaseName( lpDevices[ k ], tszCurrentDeviceName, dNameSize ) ) + { + sprintf( szCurrentDeviceName, "%S", tszCurrentDeviceName ); + + if( 0 == strcmp( strlwr( szCurrentDeviceName ), szDeviceName ) ) + { + FreePSAPIRoutines(); + + return true; + } + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void FindProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == FindProc( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void KillProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == KillProc( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void FindDevice( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == FindDev( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} diff --git a/admin/win/nsi/nsis_processes/src/processes.h b/admin/win/nsi/nsis_processes/src/processes.h new file mode 100755 index 000000000..9bd069101 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.h @@ -0,0 +1,49 @@ +#pragma once + + + + + +//------------------------------------------------------------------------------------------- +// PSAPI function pointers +typedef BOOL (WINAPI *lpfEnumProcesses) ( DWORD *, DWORD, DWORD * ); +typedef BOOL (WINAPI *lpfEnumProcessModules) ( HANDLE, HMODULE *, DWORD, LPDWORD ); +typedef DWORD (WINAPI *lpfGetModuleBaseName) ( HANDLE, HMODULE, LPTSTR, DWORD ); +typedef BOOL (WINAPI *lpfEnumDeviceDrivers) ( LPVOID *, DWORD, LPDWORD ); +typedef BOOL (WINAPI *lpfGetDeviceDriverBaseName)( LPVOID, LPTSTR, DWORD ); + + + + + + +//------------------------------------------------------------------------------------------- +// Internal use routines +bool LoadPSAPIRoutines( void ); +bool FreePSAPIRoutines( void ); + +bool FindProc( char *szProcess ); +bool KillProc( char *szProcess ); + +bool FindDev( char *szDriverName ); + + + + + +//------------------------------------------------------------------------------------------- +// Exported routines +extern "C" __declspec(dllexport) void FindProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); + +extern "C" __declspec(dllexport) void KillProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); + +extern "C" __declspec(dllexport) void FindDevice( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); diff --git a/admin/win/nsi/nsis_processes/src/processes.ncb b/admin/win/nsi/nsis_processes/src/processes.ncb new file mode 100755 index 0000000000000000000000000000000000000000..c1a5f281fe57fcc5570c40953586a2799befc159 GIT binary patch literal 44032 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 literal 0 HcmV?d00001 diff --git a/admin/win/nsi/nsis_processes/src/processes.rc b/admin/win/nsi/nsis_processes/src/processes.rc new file mode 100755 index 000000000..c6e62a3c8 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.rc @@ -0,0 +1,103 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "NSIS Plug-in for Windows process management. Only WinNT, Win2K, WinXP and Win2003 Server supported." + VALUE "CompanyName", "Andrei Ciubotaru [Hardwired]" + VALUE "FileDescription", "Windows Processes Management" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "Processes" + VALUE "LegalCopyright", "Copyright (c) 2004 Hardwired. No rights reserved." + VALUE "OriginalFilename", "Processes.dll" + VALUE "ProductName", "Processes" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/admin/win/nsi/nsis_processes/src/processes.sln b/admin/win/nsi/nsis_processes/src/processes.sln new file mode 100755 index 000000000..73fc989e2 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "processes", "processes.vcproj", "{3438467F-A719-46DC-93E5-137A8B691727}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {3438467F-A719-46DC-93E5-137A8B691727}.Debug.ActiveCfg = Debug|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Debug.Build.0 = Debug|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Release.ActiveCfg = Release|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/admin/win/nsi/nsis_processes/src/processes.txt b/admin/win/nsi/nsis_processes/src/processes.txt new file mode 100755 index 000000000..51d11902a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.txt @@ -0,0 +1,122 @@ +---------------------------------------------------------------- +---------------------------------------------------------------- +Processes (Processes.dll) +Version: 1.0.0.1 +Release: 12.december.2004 +Description:Nullsoft Installer (NSIS) plug-in for managing?! + Windows processes. + +Copyright: © 2004 Hardwired. No rights reserved. + There is no restriction and no guaranty for using + this software. + +Author: Andrei Ciubotaru [Hardwired] + Lead Developer ICode&Ideas SRL (http://www.icode.ro) + hardwiredteks@gmail.com, hardwired@icode.ro + +---------------------------------------------------------------- +---------------------------------------------------------------- +INTRODUCTION + + The Need For Plug-in - I need it for the one of my installers. + + Briefly: Use it when you need to find\kill a process when +installing\uninstalling some application. Also, use it when you +need to test the presence of a device driver. + + +SUPPORT + + Supported platforms are: WinNT,Win2K,WinXP and Win2003 Server. + + +DESCRIPTION + + Processes::FindProcess + + Searches the currently running processes for the given + process name. + + return: 1 - the process was found + 0 - the process was not found + + Processes::KillProcess + + Searches the currently running processes for the given + process name. If the process is found then the it gets + killed. + + return: 1 - the process was found and killed + 0 - the process was not found or the process + cannot be killed (insuficient rights) + + Processes::FindDevice + + Searches the installed devices drivers for the given + device base name. + (important: I said BASE NAME not FILENAME) + + return: 1 - the device driver was found + 0 - the device driver was not found + + +USAGE + + First of all, does not matter where you use it. Ofcourse, the +routines must be called inside of a Section/Function scope. + + Processes::FindProcess "process_name.exe" + Pop $R0 + + StrCmp $R0 "1" make_my_day noooooo + + make_my_day: + ... + + noooooo: + ... + + + Processes::KillProcess "process_name.exe" + Pop $R0 + + StrCmp $R0 "1" dead_meat why_wont_you_die + + dead_meat: + ... + + why_wont_you_die: + ... + + + Processes::FindDevice "device_base_name" + Pop $R0 + + StrCmp $R0 "1" blabla more_blabla + + blabla: + ... + + more_blabla: + ... + + +THANKS + + Sunil Kamath for inspiring me. I wanted to use its FindProcDLL +but my requirements made it imposible. + + Nullsoft for creating this very powerfull installer. One big, +free and full-featured (hmmm... and guiless for the moment) mean +install machine!:) + + ME for being such a great coder... + ... HAHAHAHAHAHAHA! + +ONE MORE THING + + If you use the plugin or it's source-code, I would apreciate +if my name is mentioned. + +---------------------------------------------------------------- +---------------------------------------------------------------- diff --git a/admin/win/nsi/nsis_processes/src/processes.vcproj b/admin/win/nsi/nsis_processes/src/processes.vcproj new file mode 100755 index 000000000..245cbc99f --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.vcproj @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/admin/win/nsi/nsis_processes/src/resource.h b/admin/win/nsi/nsis_processes/src/resource.h new file mode 100755 index 000000000..506377e21 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by processes.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/admin/win/nsi/page_header.bmp b/admin/win/nsi/page_header.bmp old mode 100755 new mode 100644 diff --git a/admin/win/nsi/revision.txt b/admin/win/nsi/revision.txt old mode 100644 new mode 100755 index 56749c830..c4fbb1cfa --- a/admin/win/nsi/revision.txt +++ b/admin/win/nsi/revision.txt @@ -1 +1 @@ -96 \ No newline at end of file +97 \ No newline at end of file diff --git a/admin/win/nsi/tomahawk.ini b/admin/win/nsi/tomahawk.ini old mode 100755 new mode 100644 diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi old mode 100755 new mode 100644 index 4bdb57fa4..c2d2a9bd1 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -159,6 +159,45 @@ Function LaunchTomahawkAsUser FunctionEnd !endif +############################################################################## +# # +# PROCESS HANDLING FUNCTIONS AND MACROS # +# # +############################################################################## + +!macro CheckForProcess processName gotoWhenFound gotoWhenNotFound + Processes::FindProcess ${processName} + StrCmp $R0 "0" ${gotoWhenNotFound} ${gotoWhenFound} +!macroend + +!macro ConfirmEndProcess processName + MessageBox MB_YESNO|MB_ICONEXCLAMATION \ + "Found ${processName} process(s) which need to be stopped.$\nDo you want the installer to stop these for you?" \ + IDYES process_${processName}_kill IDNO process_${processName}_ended + process_${processName}_kill: + DetailPrint "Killing ${processName} processes." + Processes::KillProcess ${processName} + Sleep 1500 + StrCmp $R0 "1" process_${processName}_ended + DetailPrint "Process to kill not found!" + process_${processName}_ended: +!macroend + +!macro CheckAndConfirmEndProcess processName + !insertmacro CheckForProcess ${processName} 0 no_process_${processName}_to_end + !insertmacro ConfirmEndProcess ${processName} + no_process_${processName}_to_end: +!macroend + +!macro EnsureTomahawkShutdown un + Function ${un}EnsureTomahawkShutdown + !insertmacro CheckAndConfirmEndProcess "tomahawk.exe" + FunctionEnd +!macroend + +!insertmacro EnsureTomahawkShutdown "" +!insertmacro EnsureTomahawkShutdown "un." + ############################################################################## # # # RE-INSTALLER FUNCTIONS # @@ -554,6 +593,16 @@ Function .onInit StrCmp $R0 0 +3 MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." Abort + + ;Use available InstallLocation when possible. This is useful in the uninstaller + ;via re-install, which would otherwise use a default location - a bug. + ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "InstallLocation" + StrCmp $R0 "" SkipSetInstDir + StrCpy $INSTDIR $R0 + SkipSetInstDir: + + ;Shutdown Tomahawk in case Add/Remove re-installer option used. + Call EnsureTomahawkShutdown FunctionEnd Function .onInstSuccess diff --git a/admin/win/nsi/welcome.bmp b/admin/win/nsi/welcome.bmp old mode 100755 new mode 100644 From a55124ed753939d131446a799695318374819d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20B=C3=A4ume?= Date: Thu, 31 Mar 2011 00:00:49 +0800 Subject: [PATCH 33/63] ship the translations within the binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build qm-files for translation create resource file for translations, contains german translations, for now. this is some kind of hack to make it work. rcc doesn’t allow out-of-source builds, so the commands need to be specified by hand, to copy things around between source and binary directories. --- lang/tomahawk_i18n.qrc | 5 +++++ lang/translations.cmake | 25 +++++++++++++++++++++++++ src/CMakeLists.txt | 4 +++- 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 lang/tomahawk_i18n.qrc create mode 100644 lang/translations.cmake diff --git a/lang/tomahawk_i18n.qrc b/lang/tomahawk_i18n.qrc new file mode 100644 index 000000000..a309b5d51 --- /dev/null +++ b/lang/tomahawk_i18n.qrc @@ -0,0 +1,5 @@ + + +tomahawk_de.qm + + diff --git a/lang/translations.cmake b/lang/translations.cmake new file mode 100644 index 000000000..a5b92f2f4 --- /dev/null +++ b/lang/translations.cmake @@ -0,0 +1,25 @@ +FILE (GLOB TS_FILES ${CMAKE_SOURCE_DIR}/lang/*.ts) +QT4_ADD_TRANSLATION(QM_FILES ${TS_FILES}) + +## HACK HACK HACK - around rcc limitations to allow out of source-tree building +SET( trans_file tomahawk_i18n ) +SET( trans_srcfile ${CMAKE_SOURCE_DIR}/lang/${trans_file}.qrc) +SET( trans_infile ${CMAKE_CURRENT_BINARY_DIR}/${trans_file}.qrc) +SET( trans_outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${trans_file}.cxx) + +# Copy the QRC file to the output directory +ADD_CUSTOM_COMMAND( + OUTPUT ${trans_infile} + COMMAND ${CMAKE_COMMAND} -E copy ${trans_srcfile} ${trans_infile} + MAIN_DEPENDENCY ${trans_srcfile} +) + +# Run the resource compiler (rcc_options should already be set) +ADD_CUSTOM_COMMAND( + OUTPUT ${trans_outfile} + COMMAND ${QT_RCC_EXECUTABLE} + ARGS ${rcc_options} -name ${trans_file} -o ${trans_outfile} ${trans_infile} + MAIN_DEPENDENCY ${trans_infile} + DEPENDS ${QM_FILES} +) + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 259756b05..69081156a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -186,7 +186,9 @@ qt4_wrap_cpp( tomahawkMoc ${tomahawkHeaders} ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) -SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} ) +include( ${CMAKE_SOURCE_DIR}/lang/translations.cmake ) + +SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} ${trans_outfile}) IF( "${gui}" STREQUAL "no" ) ELSE() From 59391e3841cc80d3e3fc9f8f951b01ab04340173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20B=C3=A4ume?= Date: Thu, 31 Mar 2011 09:06:13 +0800 Subject: [PATCH 34/63] load translation based on system locale this loads the resource that contains translations for the locale the user has set. --- src/main.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 916ffbb2e..02a3ae80d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,7 @@ */ #include "tomahawk/tomahawkapp.h" +#include #ifdef Q_WS_MAC #include "tomahawkapp_mac.h" @@ -41,6 +42,12 @@ main( int argc, char *argv[] ) try { TomahawkApp a( argc, argv ); + + QString locale = QLocale::system().name(); + + QTranslator translator; + translator.load(QString(":/lang/tomahawk_") + locale); + a.installTranslator(&translator); return a.exec(); } catch( const std::runtime_error& e ) From 0c4e304ee53c34eb6752e71eeff1a0c2ff37f3c2 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 31 Mar 2011 12:18:49 -0400 Subject: [PATCH 35/63] Make debug output a little more helpful --- src/sip/twitter/twitter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index 772c9abdb..dd10d5d2c 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -599,7 +599,7 @@ TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString, qDebug() << Q_FUNC_INFO; if ( m_attemptedConnects.contains( screenName ) && m_attemptedConnects[screenName] ) { - qDebug() << "Already attempted to connect to this peer with no change in their status, not trying again for now"; + qDebug() << "Already attempted to connect to " << screenName << " with no change in their status, not trying again for now"; return; } if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) || !peerData.contains( "node" ) ) From 47451e4fba708a334f5394127fe6a5533e14f6a4 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 31 Mar 2011 15:12:08 -0400 Subject: [PATCH 36/63] Add more debugging and remove the don't-try-to-reconnect behavior --- src/libtomahawk/network/servent.cpp | 3 +++ src/sip/twitter/twitter.cpp | 10 +--------- src/sip/twitter/twitter.h | 1 - 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index 1d54d0897..083e67d92 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -792,8 +792,11 @@ Servent::isIPWhitelisted( QHostAddress ip ) bool Servent::connectedToSession( const QString& session ) { + qDebug() << Q_FUNC_INFO; + qDebug() << "Checking against " << session; foreach( ControlConnection* cc, m_controlconnections ) { + qDebug() << "Checking session " << cc->id(); if( cc->id() == session ) return true; } diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index dd10d5d2c..7947042ae 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -178,7 +178,6 @@ TwitterPlugin::disconnectPlugin() delete m_twitterAuth.data(); m_cachedPeers.empty(); - m_attemptedConnects.empty(); m_isOnline = false; } @@ -571,7 +570,6 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q { m_cachedPeers[screenName] = QVariant::fromValue< QHash< QString, QVariant > >( _peerData ); TomahawkSettings::instance()->setTwitterCachedPeers( m_cachedPeers ); - m_attemptedConnects[screenName] = false; } if ( m_isOnline && _peerData.contains( "host" ) && _peerData.contains( "port" ) && _peerData.contains( "pkey" ) ) @@ -597,14 +595,9 @@ void TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString, QVariant > &peerData ) { qDebug() << Q_FUNC_INFO; - if ( m_attemptedConnects.contains( screenName ) && m_attemptedConnects[screenName] ) - { - qDebug() << "Already attempted to connect to " << screenName << " with no change in their status, not trying again for now"; - return; - } if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) || !peerData.contains( "node" ) ) { - qDebug() << "TwitterPlugin could not find host and/or port and/or pkey for peer " << screenName; + qDebug() << "TwitterPlugin could not find host and/or port and/or pkey and/or node for peer " << screenName; return; } QString friendlyName = QString( '@' + screenName ); @@ -614,7 +607,6 @@ TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString, peerData["pkey"].toString(), friendlyName, peerData["node"].toString() ); - m_attemptedConnects[screenName] = true; } void diff --git a/src/sip/twitter/twitter.h b/src/sip/twitter/twitter.h index 4e8a98a31..c0da00d7f 100644 --- a/src/sip/twitter/twitter.h +++ b/src/sip/twitter/twitter.h @@ -108,7 +108,6 @@ private: qint64 m_cachedMentionsSinceId; qint64 m_cachedDirectMessagesSinceId; QHash< QString, QVariant > m_cachedPeers; - QHash< QString, bool > m_attemptedConnects; QSet m_keyCache; bool m_finishedFriends; bool m_finishedMentions; From de92b0b726e54c8de8a484a7cbfb793b1b3c027b Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 31 Mar 2011 22:08:32 -0400 Subject: [PATCH 37/63] Initial work on a dir watcher for the scanner --- .../database/databasecommand_deletefiles.h | 6 ++++++ src/scanmanager.cpp | 18 ++++++++++++++++++ src/scanmanager.h | 5 +++++ 3 files changed, 29 insertions(+) diff --git a/src/libtomahawk/database/databasecommand_deletefiles.h b/src/libtomahawk/database/databasecommand_deletefiles.h index 4668d0249..53d16f314 100644 --- a/src/libtomahawk/database/databasecommand_deletefiles.h +++ b/src/libtomahawk/database/databasecommand_deletefiles.h @@ -45,6 +45,12 @@ public: setSource( source ); } + explicit DatabaseCommand_DeleteFiles( const QVariantList& ids, const Tomahawk::source_ptr& source, QObject* parent = 0 ) + : DatabaseCommandLoggable( parent ), m_ids( ids ) + { + setSource( source ); + } + virtual QString commandname() const { return "deletefiles"; } virtual void exec( DatabaseImpl* ); diff --git a/src/scanmanager.cpp b/src/scanmanager.cpp index 017dc8171..81d11969e 100644 --- a/src/scanmanager.cpp +++ b/src/scanmanager.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "musicscanner.h" #include "tomahawksettings.h" @@ -40,13 +41,20 @@ ScanManager::ScanManager( QObject* parent ) : QObject( parent ) , m_scanner( 0 ) , m_musicScannerThreadController( 0 ) + , m_dirWatcher( 0 ) { s_instance = this; + m_dirWatcher = new QFileSystemWatcher( parent ); + connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) ); + connect( m_dirWatcher, SIGNAL( directoryChanged( const QString & ) ), SLOT( handleChangedDir( const QString & ) ) ); if ( TomahawkSettings::instance()->hasScannerPath() ) m_currScannerPath = TomahawkSettings::instance()->scannerPath(); + + m_dirWatcher->addPaths( m_currScannerPath ); + qDebug() << "filewatcher dirs = " << m_dirWatcher->directories(); } @@ -83,6 +91,8 @@ ScanManager::onSettingsChanged() m_currScannerPath != TomahawkSettings::instance()->scannerPath() ) { m_currScannerPath = TomahawkSettings::instance()->scannerPath(); + m_dirWatcher->removePaths( m_dirWatcher->directories() ); + m_dirWatcher->addPaths( m_currScannerPath ); runManualScan( m_currScannerPath ); } } @@ -107,6 +117,14 @@ ScanManager::runManualScan( const QStringList& path ) } +void +ScanManager::handleChangedDir( const QString &path ) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "Dir changed: " << path; +} + + void ScanManager::scannerFinished() { diff --git a/src/scanmanager.h b/src/scanmanager.h index a20d9990f..999b629b5 100644 --- a/src/scanmanager.h +++ b/src/scanmanager.h @@ -26,6 +26,7 @@ class MusicScanner; class QThread; +class QFileSystemWatcher; class ScanManager : public QObject { @@ -42,6 +43,9 @@ public: signals: void finished(); +public slots: + void handleChangedDir( const QString &path ); + private slots: void scannerQuit(); void scannerFinished(); @@ -55,6 +59,7 @@ private: MusicScanner* m_scanner; QThread* m_musicScannerThreadController; QStringList m_currScannerPath; + QFileSystemWatcher* m_dirWatcher; }; #endif From 06f0dd87682d7154a1ebc3f3dc782ec2a0ce9b3f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 1 Apr 2011 17:05:40 -0400 Subject: [PATCH 38/63] More work on dir watchers -- it's mostly there except for the actual DB modifications --- .../database/databasecommand_dirmtimes.cpp | 26 ++++++-- .../database/databasecommand_dirmtimes.h | 7 ++ src/musicscanner.cpp | 3 + src/musicscanner.h | 2 + src/scanmanager.cpp | 64 ++++++++++++++++++- src/scanmanager.h | 8 ++- 6 files changed, 99 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.cpp b/src/libtomahawk/database/databasecommand_dirmtimes.cpp index 829a609c8..1bedc8402 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.cpp +++ b/src/libtomahawk/database/databasecommand_dirmtimes.cpp @@ -38,21 +38,33 @@ DatabaseCommand_DirMtimes::execSelect( DatabaseImpl* dbi ) { QMap mtimes; TomahawkSqlQuery query = dbi->newquery(); - if( m_prefix.isEmpty() ) + if( m_prefix.isEmpty() && m_prefixes.isEmpty() ) query.exec( "SELECT name, mtime FROM dirs_scanned" ); + else if( m_prefixes.isEmpty() ) + execSelectPath( dbi, m_prefix, mtimes ); else { - query.prepare( QString( "SELECT name, mtime " - "FROM dirs_scanned " - "WHERE name LIKE '%1%'" ).arg( m_prefix.replace( '\'',"''" ) ) ); - query.exec(); + if( !m_prefix.isEmpty() ) + execSelectPath( dbi, m_prefix, mtimes ); + foreach( QString path, m_prefixes ) + execSelectPath( dbi, path, mtimes ); } + emit done( mtimes ); +} + +void +DatabaseCommand_DirMtimes::execSelectPath( DatabaseImpl *dbi, QString &path, QMap &mtimes ) +{ + TomahawkSqlQuery query = dbi->newquery(); + query.prepare( QString( "SELECT name, mtime " + "FROM dirs_scanned " + "WHERE name LIKE '%1%'" ).arg( path.replace( '\'',"''" ) ) ); + query.exec(); + while( query.next() ) { mtimes.insert( query.value( 0 ).toString(), query.value( 1 ).toUInt() ); } - - emit done( mtimes ); } diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.h b/src/libtomahawk/database/databasecommand_dirmtimes.h index f35a78437..3ff81607b 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.h +++ b/src/libtomahawk/database/databasecommand_dirmtimes.h @@ -37,6 +37,10 @@ public: explicit DatabaseCommand_DirMtimes( const QString& prefix = "", QObject* parent = 0 ) : DatabaseCommand( parent ), m_prefix( prefix ), m_update( false ) {} + + explicit DatabaseCommand_DirMtimes( const QStringList& prefixes = QStringList(), QObject* parent = 0 ) + : DatabaseCommand( parent ), m_prefixes( prefixes ), m_update( false ) + {} explicit DatabaseCommand_DirMtimes( QMap tosave, QObject* parent = 0 ) : DatabaseCommand( parent ), m_update( true ), m_tosave( tosave ) @@ -52,9 +56,12 @@ signals: public slots: private: + void execSelectPath( DatabaseImpl *dbi, QString &path, QMap &mtimes ); + void execSelect( DatabaseImpl* dbi ); void execUpdate( DatabaseImpl* dbi ); QString m_prefix; + QStringList m_prefixes; bool m_update; QMap m_tosave; }; diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index a97171416..fe8eb29f6 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -185,9 +185,12 @@ MusicScanner::listerFinished( const QMap& newmtimes ) { qDebug() << "Removing stale dir:" << path; Database::instance()->enqueue( QSharedPointer( new DatabaseCommand_DeleteFiles( path, SourceList::instance()->getLocal() ) ) ); + emit removeWatchedDir( path ); } } + emit addWatchedDirs( newmtimes.keys() ); + // save mtimes, then quit thread DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( newmtimes ); connect( cmd, SIGNAL( finished() ), SLOT( deleteLister() ) ); diff --git a/src/musicscanner.h b/src/musicscanner.h index c4fc5bb5b..dd2725334 100644 --- a/src/musicscanner.h +++ b/src/musicscanner.h @@ -76,6 +76,8 @@ signals: //void fileScanned( QVariantMap ); void finished(); void batchReady( const QVariantList& ); + void addWatchedDirs( const QStringList & ); + void removeWatchedDir( const QString & ); private: QVariant readFile( const QFileInfo& fi ); diff --git a/src/scanmanager.cpp b/src/scanmanager.cpp index 81d11969e..e1d5f028c 100644 --- a/src/scanmanager.cpp +++ b/src/scanmanager.cpp @@ -22,11 +22,15 @@ #include #include #include +#include #include "musicscanner.h" #include "tomahawksettings.h" #include "tomahawkutils.h" +#include "database/database.h" +#include "database/databasecommand_dirmtimes.h" + ScanManager* ScanManager::s_instance = 0; @@ -53,8 +57,8 @@ ScanManager::ScanManager( QObject* parent ) if ( TomahawkSettings::instance()->hasScannerPath() ) m_currScannerPath = TomahawkSettings::instance()->scannerPath(); - m_dirWatcher->addPaths( m_currScannerPath ); - qDebug() << "filewatcher dirs = " << m_dirWatcher->directories(); + qDebug() << "loading initial directories to watch"; + QTimer::singleShot( 1000, this, SLOT( startupWatchPaths() ) ); } @@ -98,6 +102,36 @@ ScanManager::onSettingsChanged() } +void +ScanManager::startupWatchPaths() +{ + qDebug() << Q_FUNC_INFO; + + if( !Database::instance() ) + { + QTimer::singleShot( 1000, this, SLOT( startupWatchPaths() ) ); + return; + } + + DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( m_currScannerPath ); + connect( cmd, SIGNAL( done( QMap ) ), + SLOT( setInitialPaths( QMap ) ) ); + Database::instance()->enqueue( QSharedPointer(cmd) ); +} + + +void +ScanManager::setInitialPaths( QMap pathMap ) +{ + qDebug() << Q_FUNC_INFO; + foreach( QString path, pathMap.keys() ) + { + qDebug() << "Adding " << path << " to watcher"; + m_dirWatcher->addPath( path ); + } +} + + void ScanManager::runManualScan( const QStringList& path ) { @@ -109,6 +143,8 @@ ScanManager::runManualScan( const QStringList& path ) m_scanner = new MusicScanner( path ); m_scanner->moveToThread( m_musicScannerThreadController ); connect( m_scanner, SIGNAL( finished() ), SLOT( scannerFinished() ) ); + connect( m_scanner, SIGNAL( addWatchedDirs( const QStringList & ) ), SLOT( addWatchedDirs( const QStringList & ) ) ); + connect( m_scanner, SIGNAL( removeWatchedDir( const QString & ) ), SLOT( removeWatchedDir( const QString & ) ) ); m_musicScannerThreadController->start( QThread::IdlePriority ); QMetaObject::invokeMethod( m_scanner, "startScan" ); } @@ -116,9 +152,31 @@ ScanManager::runManualScan( const QStringList& path ) qDebug() << "Could not run manual scan, old scan still running"; } +void +ScanManager::addWatchedDirs( const QStringList& paths ) +{ + qDebug() << Q_FUNC_INFO; + QStringList currentWatchedPaths = m_dirWatcher->directories(); + foreach( QString path, paths ) + { + if( !currentWatchedPaths.contains( path ) ) + { + qDebug() << "adding " << path << " to watched dirs"; + m_dirWatcher->addPath( path ); + } + } +} void -ScanManager::handleChangedDir( const QString &path ) +ScanManager::removeWatchedDir( const QString& path ) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "removing " << path << " from watched dirs"; + m_dirWatcher->removePath( path ); +} + +void +ScanManager::handleChangedDir( const QString& path ) { qDebug() << Q_FUNC_INFO; qDebug() << "Dir changed: " << path; diff --git a/src/scanmanager.h b/src/scanmanager.h index 999b629b5..232627058 100644 --- a/src/scanmanager.h +++ b/src/scanmanager.h @@ -21,6 +21,7 @@ #include #include +#include #include "dllmacro.h" @@ -44,12 +45,17 @@ signals: void finished(); public slots: - void handleChangedDir( const QString &path ); + void handleChangedDir( const QString& path ); + void addWatchedDirs( const QStringList& paths ); + void removeWatchedDir( const QString& path ); + void setInitialPaths( QMap pathMap ); private slots: void scannerQuit(); void scannerFinished(); void scannerDestroyed( QObject* scanner ); + + void startupWatchPaths(); void onSettingsChanged(); From d818a7f6979adaa00cf9adee3e74ba2e8807953e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 2 Apr 2011 06:10:05 +0200 Subject: [PATCH 39/63] * Added ready() signal to Database class. --- src/libtomahawk/database/database.cpp | 3 +++ src/libtomahawk/database/database.h | 3 +++ src/libtomahawk/database/databaseimpl.cpp | 2 -- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/database/database.cpp b/src/libtomahawk/database/database.cpp index 46e61badb..691a9db3b 100644 --- a/src/libtomahawk/database/database.cpp +++ b/src/libtomahawk/database/database.cpp @@ -37,6 +37,9 @@ Database::Database( const QString& dbname, QObject* parent ) { s_instance = this; + connect( m_impl, SIGNAL( indexReady() ), SIGNAL( indexReady() ) ); + connect( m_impl, SIGNAL( indexReady() ), SIGNAL( ready() ) ); + m_workerRW->start(); } diff --git a/src/libtomahawk/database/database.h b/src/libtomahawk/database/database.h index 498f5239b..4dad4882e 100644 --- a/src/libtomahawk/database/database.h +++ b/src/libtomahawk/database/database.h @@ -41,6 +41,7 @@ class DLLEXPORT Database : public QObject { Q_OBJECT + public: static Database* instance(); @@ -54,6 +55,8 @@ public: signals: void indexReady(); // search index + void ready(); + void newJobRO( QSharedPointer ); void newJobRW( QSharedPointer ); diff --git a/src/libtomahawk/database/databaseimpl.cpp b/src/libtomahawk/database/databaseimpl.cpp index 485c7a5ef..ff96c3d7d 100644 --- a/src/libtomahawk/database/databaseimpl.cpp +++ b/src/libtomahawk/database/databaseimpl.cpp @@ -46,8 +46,6 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) , m_lastalbid( 0 ) , m_lasttrkid( 0 ) { - connect( this, SIGNAL( indexReady() ), parent, SIGNAL( indexReady() ) ); - db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" ); db.setDatabaseName( dbname ); if ( !db.open() ) From 3bc496eaafbcd5a3417150fb707ebd0d564b6825 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 2 Apr 2011 00:18:29 -0400 Subject: [PATCH 40/63] Implement watched folders and scan-on-startup. Folders are scanned after 10 seconds without a change. Also handles deferring scans of directories if attempted during an ongoing scan, both for recursive and non-recursive scans. Fixes TWK-30 / THK-30. --- src/libtomahawk/aclsystem.cpp | 2 +- src/libtomahawk/database/database.cpp | 2 + src/libtomahawk/database/database.h | 6 ++ src/libtomahawk/database/databaseimpl.h | 2 + src/libtomahawk/tomahawksettings.cpp | 35 +++++++-- src/libtomahawk/tomahawksettings.h | 9 ++- src/musicscanner.cpp | 17 +++-- src/musicscanner.h | 20 +++-- src/scanmanager.cpp | 97 ++++++++++++++++++++----- src/scanmanager.h | 18 +++-- src/settingsdialog.cpp | 6 +- src/settingsdialog.ui | 15 +++- src/tomahawkapp.cpp | 2 +- src/tomahawkwindow.cpp | 4 +- 14 files changed, 185 insertions(+), 50 deletions(-) diff --git a/src/libtomahawk/aclsystem.cpp b/src/libtomahawk/aclsystem.cpp index 7b7d70e1f..15c75a407 100644 --- a/src/libtomahawk/aclsystem.cpp +++ b/src/libtomahawk/aclsystem.cpp @@ -124,7 +124,7 @@ void ACLSystem::authorizePath( const QString& dbid, const QString& path, ACLSystem::ACL type ) { TomahawkSettings *s = TomahawkSettings::instance(); - if( !s->scannerPath().contains( path ) ) + if( !s->scannerPaths().contains( path ) ) { qDebug() << "path selected is not in our scanner path!"; return; diff --git a/src/libtomahawk/database/database.cpp b/src/libtomahawk/database/database.cpp index 691a9db3b..4436f6cfa 100644 --- a/src/libtomahawk/database/database.cpp +++ b/src/libtomahawk/database/database.cpp @@ -32,6 +32,7 @@ Database::instance() Database::Database( const QString& dbname, QObject* parent ) : QObject( parent ) + , m_ready( false ) , m_impl( new DatabaseImpl( dbname, this ) ) , m_workerRW( new DatabaseWorker( m_impl, this, true ) ) { @@ -39,6 +40,7 @@ Database::Database( const QString& dbname, QObject* parent ) connect( m_impl, SIGNAL( indexReady() ), SIGNAL( indexReady() ) ); connect( m_impl, SIGNAL( indexReady() ), SIGNAL( ready() ) ); + connect( m_impl, SIGNAL( indexReady() ), SLOT( setIsReadyTrue() ) ); m_workerRW->start(); } diff --git a/src/libtomahawk/database/database.h b/src/libtomahawk/database/database.h index 4dad4882e..a6890fbb6 100644 --- a/src/libtomahawk/database/database.h +++ b/src/libtomahawk/database/database.h @@ -52,6 +52,8 @@ public: const bool indexReady() const { return m_indexReady; } void loadIndex(); + + bool isReady() const { return m_ready; } signals: void indexReady(); // search index @@ -63,7 +65,11 @@ signals: public slots: void enqueue( QSharedPointer lc ); +private slots: + void setIsReadyTrue() { m_ready = true; } + private: + bool m_ready; DatabaseImpl* m_impl; DatabaseWorker* m_workerRW; QHash< QString, DatabaseWorker* > m_workers; diff --git a/src/libtomahawk/database/databaseimpl.h b/src/libtomahawk/database/databaseimpl.h index 579850b7e..e2965f553 100644 --- a/src/libtomahawk/database/databaseimpl.h +++ b/src/libtomahawk/database/databaseimpl.h @@ -84,6 +84,8 @@ signals: public slots: private: + bool m_ready; + bool updateSchema( int currentver ); QSqlDatabase db; diff --git a/src/libtomahawk/tomahawksettings.cpp b/src/libtomahawk/tomahawksettings.cpp index 82d04d75c..bdbe3c171 100644 --- a/src/libtomahawk/tomahawksettings.cpp +++ b/src/libtomahawk/tomahawksettings.cpp @@ -66,29 +66,50 @@ TomahawkSettings::~TomahawkSettings() QStringList -TomahawkSettings::scannerPath() const +TomahawkSettings::scannerPaths() { + //FIXME: After enough time, remove this hack (and make const) #ifndef TOMAHAWK_HEADLESS - return value( "scannerpath", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toStringList(); + if( value( "scannerpaths" ).isNull() ) + setValue( "scannerpaths", value( "scannerpath" ) ); + return value( "scannerpaths", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toStringList(); #else - return value( "scannerpath", "" ).toStringList(); + if( value( "scannerpaths" ).isNull() ) + setValue( "scannerpaths", value( "scannerpath" ) ); + return value( "scannerpaths", "" ).toStringList(); #endif } void -TomahawkSettings::setScannerPath( const QStringList& path ) +TomahawkSettings::setScannerPaths( const QStringList& paths ) { - setValue( "scannerpath", path ); + setValue( "scannerpaths", paths ); } bool -TomahawkSettings::hasScannerPath() const +TomahawkSettings::hasScannerPaths() const { - return contains( "scannerpath" ); + //FIXME: After enough time, remove this hack + return contains( "scannerpaths" ) || contains( "scannerpath" ); } + +bool +TomahawkSettings::watchForChanges() const +{ + return value( "watchForChanges", true ).toBool(); +} + + +void +TomahawkSettings::setWatchForChanges( bool watch ) +{ + setValue( "watchForChanges", watch ); +} + + void TomahawkSettings::setAcceptedLegalWarning( bool accept ) { diff --git a/src/libtomahawk/tomahawksettings.h b/src/libtomahawk/tomahawksettings.h index cc2b2923f..42daa91e3 100644 --- a/src/libtomahawk/tomahawksettings.h +++ b/src/libtomahawk/tomahawksettings.h @@ -41,9 +41,12 @@ public: void applyChanges() { emit changed(); } /// General settings - QStringList scannerPath() const; /// QDesktopServices::MusicLocation by default - void setScannerPath( const QStringList& path ); - bool hasScannerPath() const; + QStringList scannerPaths(); /// QDesktopServices::MusicLocation by default + void setScannerPaths( const QStringList& paths ); + bool hasScannerPaths() const; + + bool watchForChanges() const; + void setWatchForChanges( bool watch ); bool acceptedLegalWarning() const; void setAcceptedLegalWarning( bool accept ); diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index fe8eb29f6..1662b452c 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -31,14 +31,16 @@ using namespace Tomahawk; void DirLister::go() { - scanDir( m_dir, 0 ); + foreach( QString dir, m_dirs ) + scanDir( QDir( dir, 0 ), 0, ( m_recursive ? DirLister::Recursive : DirLister::NonRecursive ) ); emit finished( m_newdirmtimes ); } void -DirLister::scanDir( QDir dir, int depth ) +DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) { + qDebug() << "DirLister::scanDir scanning: " << dir.absolutePath(); QFileInfoList dirs; const uint mtime = QFileInfo( dir.absolutePath() ).lastModified().toUTC().toTime_t(); m_newdirmtimes.insert( dir.absolutePath(), mtime ); @@ -65,14 +67,18 @@ DirLister::scanDir( QDir dir, int depth ) foreach( const QFileInfo& di, dirs ) { - scanDir( di.absoluteFilePath(), depth + 1 ); + if( mode == DirLister::Recursive || !m_dirmtimes.contains( di.absolutePath() ) ) + scanDir( di.absoluteFilePath(), depth + 1, DirLister::Recursive ); + else //should be the non-recursive case since the second test above should only happen with a new dir + scanDir( di.absoluteFilePath(), depth + 1, DirLister::MTimeOnly ); } } -MusicScanner::MusicScanner( const QStringList& dirs, quint32 bs ) +MusicScanner::MusicScanner( const QStringList& dirs, bool recursive, quint32 bs ) : QObject() , m_dirs( dirs ) + , m_recursive( recursive ) , m_batchsize( bs ) , m_dirLister( 0 ) , m_dirListerThreadController( 0 ) @@ -150,8 +156,7 @@ MusicScanner::scan() m_dirListerThreadController = new QThread( this ); - //FIXME: MULTIPLECOLLECTIONDIRS - m_dirLister = new DirLister( QDir( m_dirs.first(), 0 ), m_dirmtimes ); + m_dirLister = new DirLister( m_dirs, m_dirmtimes, m_recursive ); m_dirLister->moveToThread( m_dirListerThreadController ); connect( m_dirLister, SIGNAL( fileToScan( QFileInfo ) ), diff --git a/src/musicscanner.h b/src/musicscanner.h index dd2725334..77fb62071 100644 --- a/src/musicscanner.h +++ b/src/musicscanner.h @@ -39,8 +39,15 @@ class DirLister : public QObject Q_OBJECT public: - DirLister( QDir d, QMap& mtimes ) - : QObject(), m_dir( d ), m_dirmtimes( mtimes ) + + enum Mode { + NonRecursive, + Recursive, + MTimeOnly + }; + + DirLister( QStringList dirs, QMap& mtimes, bool recursive ) + : QObject(), m_dirs( dirs ), m_dirmtimes( mtimes ), m_recursive( recursive ) { qDebug() << Q_FUNC_INFO; } @@ -56,11 +63,13 @@ signals: private slots: void go(); - void scanDir( QDir dir, int depth ); + void scanDir( QDir dir, int depth, DirLister::Mode mode ); private: - QDir m_dir; + QStringList m_dirs; QMap m_dirmtimes; + bool m_recursive; + QMap m_newdirmtimes; }; @@ -69,7 +78,7 @@ class MusicScanner : public QObject Q_OBJECT public: - MusicScanner( const QStringList& dirs, quint32 bs = 0 ); + MusicScanner( const QStringList& dirs, bool recursive = true, quint32 bs = 0 ); ~MusicScanner(); signals: @@ -105,6 +114,7 @@ private: QMap m_newdirmtimes; QList m_scannedfiles; + bool m_recursive; quint32 m_batchsize; DirLister* m_dirLister; diff --git a/src/scanmanager.cpp b/src/scanmanager.cpp index e1d5f028c..5fda0a7ec 100644 --- a/src/scanmanager.cpp +++ b/src/scanmanager.cpp @@ -45,20 +45,33 @@ ScanManager::ScanManager( QObject* parent ) : QObject( parent ) , m_scanner( 0 ) , m_musicScannerThreadController( 0 ) + , m_currScannerPaths() , m_dirWatcher( 0 ) + , m_queuedScanTimer( 0 ) + , m_deferredScanTimer( 0 ) + , m_queuedChangedDirs() + , m_deferredDirs() { s_instance = this; - m_dirWatcher = new QFileSystemWatcher( parent ); + m_queuedScanTimer = new QTimer( this ); + m_queuedScanTimer->setSingleShot( true ); + m_deferredScanTimer = new QTimer( this ); + m_deferredScanTimer->setSingleShot( false ); + m_deferredScanTimer->setInterval( 1000 ); + m_dirWatcher = new QFileSystemWatcher( this ); connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) ); + connect( m_queuedScanTimer, SIGNAL( timeout() ), SLOT( queuedScanTimeout() ) ); + connect( m_deferredScanTimer, SIGNAL( timeout() ), SLOT( deferredScanTimeout() ) ); connect( m_dirWatcher, SIGNAL( directoryChanged( const QString & ) ), SLOT( handleChangedDir( const QString & ) ) ); - if ( TomahawkSettings::instance()->hasScannerPath() ) - m_currScannerPath = TomahawkSettings::instance()->scannerPath(); + if ( TomahawkSettings::instance()->hasScannerPaths() ) + m_currScannerPaths = TomahawkSettings::instance()->scannerPaths(); qDebug() << "loading initial directories to watch"; QTimer::singleShot( 1000, this, SLOT( startupWatchPaths() ) ); + m_deferredScanTimer->start(); } @@ -91,14 +104,18 @@ ScanManager::~ScanManager() void ScanManager::onSettingsChanged() { - if ( TomahawkSettings::instance()->hasScannerPath() && - m_currScannerPath != TomahawkSettings::instance()->scannerPath() ) + if ( TomahawkSettings::instance()->hasScannerPaths() && + m_currScannerPaths != TomahawkSettings::instance()->scannerPaths() ) { - m_currScannerPath = TomahawkSettings::instance()->scannerPath(); + m_currScannerPaths = TomahawkSettings::instance()->scannerPaths(); m_dirWatcher->removePaths( m_dirWatcher->directories() ); - m_dirWatcher->addPaths( m_currScannerPath ); - runManualScan( m_currScannerPath ); + m_dirWatcher->addPaths( m_currScannerPaths ); + runManualScan( m_currScannerPaths ); } + + if( TomahawkSettings::instance()->watchForChanges() && + !m_queuedChangedDirs.isEmpty() ) + runManualScan( m_queuedChangedDirs, false ); } @@ -107,21 +124,21 @@ ScanManager::startupWatchPaths() { qDebug() << Q_FUNC_INFO; - if( !Database::instance() ) + if( !Database::instance() || ( Database::instance() && !Database::instance()->isReady() ) ) { QTimer::singleShot( 1000, this, SLOT( startupWatchPaths() ) ); return; } - DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( m_currScannerPath ); - connect( cmd, SIGNAL( done( QMap ) ), - SLOT( setInitialPaths( QMap ) ) ); - Database::instance()->enqueue( QSharedPointer(cmd) ); + DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( m_currScannerPaths ); + connect( cmd, SIGNAL( done( QMap< QString, unsigned int > ) ), + SLOT( setInitialPaths( QMap< QString, unsigned int > ) ) ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); } void -ScanManager::setInitialPaths( QMap pathMap ) +ScanManager::setInitialPaths( QMap< QString, unsigned int > pathMap ) { qDebug() << Q_FUNC_INFO; foreach( QString path, pathMap.keys() ) @@ -129,27 +146,45 @@ ScanManager::setInitialPaths( QMap pathMap ) qDebug() << "Adding " << path << " to watcher"; m_dirWatcher->addPath( path ); } + runManualScan( TomahawkSettings::instance()->scannerPaths() ); } void -ScanManager::runManualScan( const QStringList& path ) +ScanManager::runManualScan( const QStringList& paths, bool recursive ) { qDebug() << Q_FUNC_INFO; if ( !m_musicScannerThreadController && !m_scanner ) //still running if these are not zero { m_musicScannerThreadController = new QThread( this ); - m_scanner = new MusicScanner( path ); + QStringList allPaths = paths; + foreach( QString path, m_deferredDirs[recursive] ) + { + if( !allPaths.contains( path ) ) + allPaths << path; + } + m_scanner = new MusicScanner( paths, recursive ); m_scanner->moveToThread( m_musicScannerThreadController ); connect( m_scanner, SIGNAL( finished() ), SLOT( scannerFinished() ) ); connect( m_scanner, SIGNAL( addWatchedDirs( const QStringList & ) ), SLOT( addWatchedDirs( const QStringList & ) ) ); connect( m_scanner, SIGNAL( removeWatchedDir( const QString & ) ), SLOT( removeWatchedDir( const QString & ) ) ); m_musicScannerThreadController->start( QThread::IdlePriority ); QMetaObject::invokeMethod( m_scanner, "startScan" ); + m_deferredDirs[recursive].clear(); } else - qDebug() << "Could not run manual scan, old scan still running"; + { + qDebug() << "Could not run manual scan, old scan still running; deferring paths"; + foreach( QString path, paths ) + { + if( !m_deferredDirs[recursive].contains( path ) ) + { + qDebug() << "Deferring path " << path; + m_deferredDirs[recursive] << path; + } + } + } } void @@ -180,6 +215,34 @@ ScanManager::handleChangedDir( const QString& path ) { qDebug() << Q_FUNC_INFO; qDebug() << "Dir changed: " << path; + m_queuedChangedDirs << path; + if( TomahawkSettings::instance()->watchForChanges() ) + m_queuedScanTimer->start( 10000 ); +} + + +void +ScanManager::queuedScanTimeout() +{ + qDebug() << Q_FUNC_INFO; + runManualScan( m_queuedChangedDirs, false ); + m_queuedChangedDirs.clear(); +} + + +void +ScanManager::deferredScanTimeout() +{ + if( !m_deferredDirs[true].isEmpty() ) + { + qDebug() << "Running scan for deferred recursive paths"; + runManualScan( m_deferredDirs[true], true ); + } + else if( !m_deferredDirs[false].isEmpty() ) + { + qDebug() << "Running scan for deferred non-recursive paths"; + runManualScan( m_deferredDirs[false], false ); + } } diff --git a/src/scanmanager.h b/src/scanmanager.h index 232627058..fdec0cf15 100644 --- a/src/scanmanager.h +++ b/src/scanmanager.h @@ -19,15 +19,17 @@ #ifndef SCANMANAGER_H #define SCANMANAGER_H +#include +#include #include #include -#include #include "dllmacro.h" class MusicScanner; class QThread; class QFileSystemWatcher; +class QTimer; class ScanManager : public QObject { @@ -38,17 +40,16 @@ public: explicit ScanManager( QObject* parent = 0 ); virtual ~ScanManager(); - - void runManualScan( const QStringList& path ); signals: void finished(); public slots: + void runManualScan( const QStringList& paths, bool recursive = true ); void handleChangedDir( const QString& path ); void addWatchedDirs( const QStringList& paths ); void removeWatchedDir( const QString& path ); - void setInitialPaths( QMap pathMap ); + void setInitialPaths( QMap< QString, unsigned int > pathMap ); private slots: void scannerQuit(); @@ -56,6 +57,8 @@ private slots: void scannerDestroyed( QObject* scanner ); void startupWatchPaths(); + void queuedScanTimeout(); + void deferredScanTimeout(); void onSettingsChanged(); @@ -64,8 +67,13 @@ private: MusicScanner* m_scanner; QThread* m_musicScannerThreadController; - QStringList m_currScannerPath; + QStringList m_currScannerPaths; QFileSystemWatcher* m_dirWatcher; + + QTimer* m_queuedScanTimer; + QTimer* m_deferredScanTimer; + QStringList m_queuedChangedDirs; + QHash< bool, QStringList > m_deferredDirs; }; #endif diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 58fbd1f96..b8808b621 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -87,7 +87,8 @@ SettingsDialog::SettingsDialog( QWidget *parent ) // MUSIC SCANNER //FIXME: MULTIPLECOLLECTIONDIRS - ui->lineEditMusicPath->setText( s->scannerPath().first() ); + ui->lineEditMusicPath->setText( s->scannerPaths().first() ); + ui->checkBoxWatchForChanges->setChecked( s->watchForChanges() ); // LAST FM ui->checkBoxEnableLastfm->setChecked( s->scrobblingEnabled() ); @@ -134,7 +135,8 @@ SettingsDialog::~SettingsDialog() s->setExternalHostname( ui->staticHostName->text() ); s->setExternalPort( ui->staticPort->value() ); - s->setScannerPath( QStringList( ui->lineEditMusicPath->text() ) ); + s->setScannerPaths( QStringList( ui->lineEditMusicPath->text() ) ); + s->setWatchForChanges( ui->checkBoxWatchForChanges->isChecked() ); s->setScrobblingEnabled( ui->checkBoxEnableLastfm->isChecked() ); s->setLastFmUsername( ui->lineEditLastfmUsername->text() ); diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index c3f302da8..0558568b6 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -66,7 +66,7 @@ - e.g. user@example.com + e.g. user@example.com @@ -454,6 +454,19 @@ + + + + + 0 + 0 + + + + Watch for changes + + + diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 46c002c4a..a148d9a02 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -285,7 +285,7 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) } #ifndef TOMAHAWK_HEADLESS - if ( !TomahawkSettings::instance()->hasScannerPath() ) + if ( !TomahawkSettings::instance()->hasScannerPaths() ) { m_mainwindow->showSettingsDialog(); } diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 843d8b525..15b355d28 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -319,8 +319,8 @@ TomahawkWindow::showSettingsDialog() void TomahawkWindow::updateCollectionManually() { - if ( TomahawkSettings::instance()->hasScannerPath() ) - ScanManager::instance()->runManualScan( TomahawkSettings::instance()->scannerPath() ); + if ( TomahawkSettings::instance()->hasScannerPaths() ) + ScanManager::instance()->runManualScan( TomahawkSettings::instance()->scannerPaths() ); } From 569c23a30e819ca75aa99a93c79f423bfa8f3283 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 2 Apr 2011 00:56:36 -0400 Subject: [PATCH 41/63] Whitespacing --- src/scanmanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/scanmanager.cpp b/src/scanmanager.cpp index 5fda0a7ec..5f848966e 100644 --- a/src/scanmanager.cpp +++ b/src/scanmanager.cpp @@ -187,6 +187,7 @@ ScanManager::runManualScan( const QStringList& paths, bool recursive ) } } + void ScanManager::addWatchedDirs( const QStringList& paths ) { @@ -202,6 +203,7 @@ ScanManager::addWatchedDirs( const QStringList& paths ) } } + void ScanManager::removeWatchedDir( const QString& path ) { @@ -210,6 +212,7 @@ ScanManager::removeWatchedDir( const QString& path ) m_dirWatcher->removePath( path ); } + void ScanManager::handleChangedDir( const QString& path ) { From 2214b750be0316f86e280c6a851ccc244b767a36 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 2 Apr 2011 00:58:43 -0400 Subject: [PATCH 42/63] Changelogify --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index c3a791448..6cf41a2ca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version 0.1.0: + * Watch folders for changes and automatically update your collection. This + is on by default; you can turn it off on the Local Music tab in the + settings dialog. + Version 0.0.3: * Fix crashes in Twitter authentication. For reals now. * Properly honor the chosen port number if a static host and port are From 01f2fe6adc3b1932d0829b2f038d68782b8616b1 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 2 Apr 2011 01:17:48 -0400 Subject: [PATCH 43/63] Fix missing condition, wrong method, and put more info in the ChangeLog --- ChangeLog | 5 ++++- src/musicscanner.cpp | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6cf41a2ca..f3363512e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,10 @@ Version 0.1.0: * Watch folders for changes and automatically update your collection. This is on by default; you can turn it off on the Local Music tab in the - settings dialog. + settings dialog. Note that this triggers only on files or folders being + added to or removed from folders; it is not watch individual files as + most OSes can't support enough file watches to handle a normal-sized + music collection. Version 0.0.3: * Fix crashes in Twitter authentication. For reals now. diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index 1662b452c..6ccbf14ae 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -40,7 +40,7 @@ DirLister::go() void DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) { - qDebug() << "DirLister::scanDir scanning: " << dir.absolutePath(); + qDebug() << "DirLister::scanDir scanning: " << dir.absolutePath() << " with mode " << mode; QFileInfoList dirs; const uint mtime = QFileInfo( dir.absolutePath() ).lastModified().toUTC().toTime_t(); m_newdirmtimes.insert( dir.absolutePath(), mtime ); @@ -49,9 +49,9 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) { // dont scan this dir, unchanged since last time. } - else + else if( mode != DirLister::MTimeOnly ) { - if ( m_dirmtimes.contains( dir.absolutePath() ) ) + if( m_dirmtimes.contains( dir.absolutePath() ) ) Database::instance()->enqueue( QSharedPointer( new DatabaseCommand_DeleteFiles( dir, SourceList::instance()->getLocal() ) ) ); dir.setFilter( QDir::Files | QDir::Readable | QDir::NoDotAndDotDot ); @@ -64,10 +64,10 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) } dir.setFilter( QDir::Dirs | QDir::Readable | QDir::NoDotAndDotDot ); dirs = dir.entryInfoList(); - + foreach( const QFileInfo& di, dirs ) { - if( mode == DirLister::Recursive || !m_dirmtimes.contains( di.absolutePath() ) ) + if( mode == DirLister::Recursive || !m_dirmtimes.contains( di.absoluteFilePath() ) ) scanDir( di.absoluteFilePath(), depth + 1, DirLister::Recursive ); else //should be the non-recursive case since the second test above should only happen with a new dir scanDir( di.absoluteFilePath(), depth + 1, DirLister::MTimeOnly ); From 6af30bfd2f4c7ee15fa2be39ec655cd827f7732f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 2 Apr 2011 06:25:16 +0200 Subject: [PATCH 44/63] * Cleaned up style. --- src/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 02a3ae80d..b5c84dae6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,6 +39,7 @@ main( int argc, char *argv[] ) AEInstallEventHandler( 'GURL', 'GURL', h, 0, false ); #endif + try { TomahawkApp a( argc, argv ); @@ -46,8 +47,8 @@ main( int argc, char *argv[] ) QString locale = QLocale::system().name(); QTranslator translator; - translator.load(QString(":/lang/tomahawk_") + locale); - a.installTranslator(&translator); + translator.load( QString( ":/lang/tomahawk_" ) + locale ); + a.installTranslator( &translator ); return a.exec(); } catch( const std::runtime_error& e ) From 0a593aeb0556a9e071d74ddb78db531395e1d5be Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 2 Apr 2011 10:24:20 +0200 Subject: [PATCH 45/63] * Only keep track of the 50 most recent tracks in WelcomeWidget. Don't auto-resolve playback-logs any longer. --- .../database/databasecommand_logplayback.cpp | 3 ++- src/libtomahawk/playlist/playlistmodel.cpp | 10 +++++--- src/libtomahawk/playlist/playlistmodel.h | 1 + src/libtomahawk/playlist/trackmodel.cpp | 14 +++++++++++ src/libtomahawk/playlist/trackmodel.h | 2 ++ src/libtomahawk/widgets/welcomewidget.cpp | 24 +++++++++++++++++-- src/libtomahawk/widgets/welcomewidget.h | 3 +++ 7 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/database/databasecommand_logplayback.cpp b/src/libtomahawk/database/databasecommand_logplayback.cpp index 9254ea979..6443b2216 100644 --- a/src/libtomahawk/database/databasecommand_logplayback.cpp +++ b/src/libtomahawk/database/databasecommand_logplayback.cpp @@ -44,7 +44,8 @@ DatabaseCommand_LogPlayback::postCommitHook() connect( this, SIGNAL( trackPlayed( Tomahawk::query_ptr ) ), source().data(), SLOT( onPlaybackFinished( Tomahawk::query_ptr ) ), Qt::QueuedConnection ); - Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString(), uuid() ); + // do not auto resolve this track + Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString() ); if ( m_action == Finished ) { diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index 8e4d24ae6..2ab23bf51 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -396,12 +396,16 @@ PlaylistModel::playlistEntries() const } +void +PlaylistModel::remove( unsigned int row, bool moreToCome ) +{ + removeIndex( index( row, 0, QModelIndex() ), moreToCome ); +} + + void PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome ) { - if ( isReadOnly() ) - return; - TrackModel::removeIndex( index ); if ( !moreToCome && !m_playlist.isNull() ) diff --git a/src/libtomahawk/playlist/playlistmodel.h b/src/libtomahawk/playlist/playlistmodel.h index e69bf59e1..bc743a18a 100644 --- a/src/libtomahawk/playlist/playlistmodel.h +++ b/src/libtomahawk/playlist/playlistmodel.h @@ -62,6 +62,7 @@ public: void insert( unsigned int row, const Tomahawk::query_ptr& query ); + void remove( unsigned int row, bool moreToCome = false ); virtual void removeIndex( const QModelIndex& index, bool moreToCome = false ); signals: diff --git a/src/libtomahawk/playlist/trackmodel.cpp b/src/libtomahawk/playlist/trackmodel.cpp index eceadd401..e3fd2083a 100644 --- a/src/libtomahawk/playlist/trackmodel.cpp +++ b/src/libtomahawk/playlist/trackmodel.cpp @@ -26,6 +26,7 @@ #include "utils/tomahawkutils.h" #include "album.h" +#include "pipeline.h" using namespace Tomahawk; @@ -370,3 +371,16 @@ TrackModel::onPlaybackStopped() oldEntry->setIsPlaying( false ); } } + + +void +TrackModel::ensureResolved() +{ + for( int i = 0; i < rowCount( QModelIndex() ); i++ ) + { + query_ptr query = itemFromIndex( index( i, 0, QModelIndex() ) )->query(); + + if ( !query->numResults() ) + Pipeline::instance()->resolve( query ); + } +} diff --git a/src/libtomahawk/playlist/trackmodel.h b/src/libtomahawk/playlist/trackmodel.h index d9cec03d8..b3a5e0c24 100644 --- a/src/libtomahawk/playlist/trackmodel.h +++ b/src/libtomahawk/playlist/trackmodel.h @@ -76,6 +76,8 @@ public: virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } virtual bool shuffled() const { return false; } + virtual void ensureResolved(); + virtual void append( const Tomahawk::query_ptr& query ) = 0; PlItem* itemFromIndex( const QModelIndex& index ) const; diff --git a/src/libtomahawk/widgets/welcomewidget.cpp b/src/libtomahawk/widgets/welcomewidget.cpp index cc4460895..d7ad62938 100644 --- a/src/libtomahawk/widgets/welcomewidget.cpp +++ b/src/libtomahawk/widgets/welcomewidget.cpp @@ -31,7 +31,9 @@ #include -#define FILTER_TIMEOUT 280 +#define HISTORY_TRACK_ITEMS 50 +#define HISTORY_PLAYLIST_ITEMS 10 +#define HISTORY_RESOLVING_TIMEOUT 2500 WelcomeWidget::WelcomeWidget( QWidget* parent ) @@ -46,7 +48,10 @@ WelcomeWidget::WelcomeWidget( QWidget* parent ) m_tracksModel = new PlaylistModel( ui->tracksView ); ui->tracksView->setModel( m_tracksModel ); - m_tracksModel->loadHistory( Tomahawk::source_ptr() ); + m_tracksModel->loadHistory( Tomahawk::source_ptr(), HISTORY_TRACK_ITEMS ); + + m_timer = new QTimer( this ); + connect( m_timer, SIGNAL( timeout() ), SLOT( checkQueries() ) ); connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) ); @@ -96,10 +101,25 @@ WelcomeWidget::onSourceAdded( const Tomahawk::source_ptr& source ) } +void +WelcomeWidget::checkQueries() +{ + m_timer->stop(); + m_tracksModel->ensureResolved(); +} + + void WelcomeWidget::onPlaybackFinished( const Tomahawk::query_ptr& query ) { m_tracksModel->insert( 0, query ); + + if ( m_tracksModel->trackCount() > HISTORY_TRACK_ITEMS ) + m_tracksModel->remove( HISTORY_TRACK_ITEMS ); + + if ( m_timer->isActive() ) + m_timer->stop(); + m_timer->start( HISTORY_RESOLVING_TIMEOUT ); } diff --git a/src/libtomahawk/widgets/welcomewidget.h b/src/libtomahawk/widgets/welcomewidget.h index 9ec4e249c..036e3d5ca 100644 --- a/src/libtomahawk/widgets/welcomewidget.h +++ b/src/libtomahawk/widgets/welcomewidget.h @@ -124,10 +124,13 @@ private slots: void onPlaylistActivated( QListWidgetItem* item ); void onPlaybackFinished( const Tomahawk::query_ptr& query ); + void checkQueries(); + private: Ui::WelcomeWidget *ui; PlaylistModel* m_tracksModel; + QTimer* m_timer; }; #endif // WELCOMEWIDGET_H From 5255fd2a7861490a68ea7cc477b585e2f637b4c2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 2 Apr 2011 10:24:48 +0200 Subject: [PATCH 46/63] * Properly thread QtScriptResolver. --- src/resolvers/qtscriptresolver.cpp | 126 +++++++++++++++++++++-------- src/resolvers/qtscriptresolver.h | 39 +++++++-- 2 files changed, 125 insertions(+), 40 deletions(-) diff --git a/src/resolvers/qtscriptresolver.cpp b/src/resolvers/qtscriptresolver.cpp index d4c0cc5f5..a19147b14 100644 --- a/src/resolvers/qtscriptresolver.cpp +++ b/src/resolvers/qtscriptresolver.cpp @@ -27,19 +27,86 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) : Tomahawk::ExternalResolver( scriptPath ) - , m_engine( new ScriptEngine( this ) ) - , m_thread( new QThread( this ) ) , m_ready( false ) , m_stopped( false ) { qDebug() << Q_FUNC_INFO << scriptPath; + m_thread = new ScriptThread( scriptPath, this ); + connect( m_thread, SIGNAL( engineFound( QString, unsigned int, unsigned int, unsigned int ) ), + SLOT( onEngineFound( QString, unsigned int, unsigned int, unsigned int ) ) ); + m_thread->start(); - QFile scriptFile( scriptPath ); + connect( this, SIGNAL( destroyed( QObject* ) ), m_thread, SLOT( deleteLater() ) ); +} + + +QtScriptResolver::~QtScriptResolver() +{ + Tomahawk::Pipeline::instance()->removeResolver( this ); + delete m_thread; +} + + +void +QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) +{ + m_thread->resolve( query ); +} + + +void +QtScriptResolver::onEngineFound( const QString& name, unsigned int weight, unsigned int timeout, unsigned int preference ) +{ + m_name = name; + m_weight = weight; + m_timeout = timeout; + m_preference = preference; + + qDebug() << "QTSCRIPT" << filePath() << "READY," << endl + << "name" << m_name << endl + << "weight" << m_weight << endl + << "timeout" << m_timeout << endl + << "preference" << m_preference; + + m_ready = true; + Tomahawk::Pipeline::instance()->addResolver( this ); +} + + +ScriptThread::ScriptThread( const QString& scriptPath, QtScriptResolver* parent ) + : QThread() + , m_parent( parent ) + , m_scriptPath( scriptPath ) +{ + moveToThread( this ); +} + + +void +ScriptThread::resolve( const Tomahawk::query_ptr& query ) +{ + m_engine->resolve( query ); +} + + +void +ScriptThread::run() +{ + QTimer::singleShot( 0, this, SLOT( initEngine() ) ); + exec(); +} + + +void +ScriptThread::initEngine() +{ + m_engine = new ScriptEngine( m_parent, this ); + QFile scriptFile( m_scriptPath ); if ( !scriptFile.open( QIODevice::ReadOnly ) ) { - qDebug() << Q_FUNC_INFO << "Failed loading JavaScript resolver:" << scriptPath; + qDebug() << Q_FUNC_INFO << "Failed loading JavaScript resolver:" << m_scriptPath; deleteLater(); return; } @@ -48,43 +115,32 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) m_engine->mainFrame()->evaluateJavaScript( scriptFile.readAll() ); scriptFile.close(); + QString name; + unsigned int weight, preference, timeout; QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( "getSettings();" ).toMap(); - m_name = m.value( "name" ).toString(); - m_weight = m.value( "weight", 0 ).toUInt(); - m_timeout = m.value( "timeout", 25 ).toUInt() * 1000; - m_preference = m.value( "preference", 0 ).toUInt(); + name = m.value( "name" ).toString(); + weight = m.value( "weight", 0 ).toUInt(); + timeout = m.value( "timeout", 25 ).toUInt() * 1000; + preference = m.value( "preference", 0 ).toUInt(); - qDebug() << "QTSCRIPT" << filePath() << "READY," << endl - << "name" << m_name << endl - << "weight" << m_weight << endl - << "timeout" << m_timeout << endl - << "preference" << m_preference; - - m_engine->moveToThread( m_thread ); - m_ready = true; - Tomahawk::Pipeline::instance()->addResolver( this ); - - connect( this, SIGNAL( destroyed( QObject* ) ), m_thread, SLOT( deleteLater() ) ); -} - - -QtScriptResolver::~QtScriptResolver() -{ - Tomahawk::Pipeline::instance()->removeResolver( this ); - delete m_engine; -} - - -void -QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) -{ - QMetaObject::invokeMethod( m_engine, "resolve", Qt::QueuedConnection, Q_ARG( Tomahawk::query_ptr, query ) ); + qDebug() << Q_FUNC_INFO << name << weight << timeout << preference; + emit engineFound( name, weight, timeout, preference ); } void ScriptEngine::resolve( const Tomahawk::query_ptr& query ) { + if ( QThread::currentThread() != thread() ) + { +// qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; + QMetaObject::invokeMethod( this, "resolve", + Qt::QueuedConnection, + Q_ARG(Tomahawk::query_ptr, query) + ); + return; + } + qDebug() << Q_FUNC_INFO << query->toString(); QString eval = QString( "resolve( '%1', '%2', '%3', '%4' );" ) .arg( query->id().replace( "'", "\\'" ) ) @@ -113,9 +169,9 @@ ScriptEngine::resolve( const Tomahawk::query_ptr& query ) rp->setBitrate( m.value( "bitrate" ).toUInt() ); rp->setUrl( m.value( "url" ).toString() ); rp->setSize( m.value( "size" ).toUInt() ); - rp->setScore( m.value( "score" ).toFloat() * ( (float)m_parent->weight() / 100.0 ) ); + rp->setScore( m.value( "score" ).toFloat() * ( (float)m_resolver->weight() / 100.0 ) ); rp->setRID( uuid() ); - rp->setFriendlySource( m_parent->name() ); + rp->setFriendlySource( m_resolver->name() ); if ( m.contains( "year" ) ) { diff --git a/src/resolvers/qtscriptresolver.h b/src/resolvers/qtscriptresolver.h index 05b55cc71..7c69fe103 100644 --- a/src/resolvers/qtscriptresolver.h +++ b/src/resolvers/qtscriptresolver.h @@ -26,9 +26,11 @@ #include #include #include +#include #include #include +class ScriptThread; class QtScriptResolver; class ScriptEngine : public QWebPage @@ -36,9 +38,10 @@ class ScriptEngine : public QWebPage Q_OBJECT public: - explicit ScriptEngine( QtScriptResolver* parent ) + explicit ScriptEngine( QtScriptResolver* resolver, ScriptThread* parent ) : QWebPage( (QObject*)parent ) , m_parent( parent ) + , m_resolver( resolver ) {} public slots: @@ -54,9 +57,35 @@ protected: { qDebug() << "JAVASCRIPT ERROR:" << message << lineNumber << sourceID; } private: - QtScriptResolver* m_parent; + ScriptThread* m_parent; + QtScriptResolver* m_resolver; }; + +class ScriptThread : public QThread +{ +Q_OBJECT + +public: + ScriptThread( const QString& scriptPath, QtScriptResolver* parent ); + + void run(); + + virtual void resolve( const Tomahawk::query_ptr& query ); + +signals: + void engineFound( const QString& name, unsigned int weight, unsigned int timeout, unsigned int preference ); + +private slots: + void initEngine(); + +private: + ScriptEngine* m_engine; + QtScriptResolver* m_parent; + QString m_scriptPath; +}; + + class QtScriptResolver : public Tomahawk::ExternalResolver { Q_OBJECT @@ -76,12 +105,12 @@ public slots: signals: void finished(); - + private slots: + void onEngineFound( const QString& name, unsigned int weight, unsigned int timeout, unsigned int preference ); private: - ScriptEngine* m_engine; - QThread* m_thread; + ScriptThread* m_thread; QString m_name; unsigned int m_weight, m_preference, m_timeout; From ea744456c9d5e47e50359a1bcce5e06989b6860c Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 2 Apr 2011 11:07:04 -0400 Subject: [PATCH 47/63] Don't start scanning on a fresh config/db --- src/scanmanager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scanmanager.cpp b/src/scanmanager.cpp index 5f848966e..945d42987 100644 --- a/src/scanmanager.cpp +++ b/src/scanmanager.cpp @@ -146,7 +146,8 @@ ScanManager::setInitialPaths( QMap< QString, unsigned int > pathMap ) qDebug() << "Adding " << path << " to watcher"; m_dirWatcher->addPath( path ); } - runManualScan( TomahawkSettings::instance()->scannerPaths() ); + if( TomahawkSettings::instance()->hasScannerPaths() ) + runManualScan( TomahawkSettings::instance()->scannerPaths() ); } From d2e867b9eeaf5082ad288a5356eb753ffa3cc292 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 2 Apr 2011 13:44:47 -0400 Subject: [PATCH 48/63] switch to using KDSingleApplicationGuard instead of QtUniqueApp as it's broken on windows. KDSingleApplicationGuard is from KDTools and this is the GPL-licensed version. --- include/tomahawk/tomahawkapp.h | 5 +- src/headlesscheck.h | 8 +- src/libtomahawk/CMakeLists.txt | 12 +- .../kdlockedsharedmemorypointer.cpp | 475 +++++++++++++ .../kdlockedsharedmemorypointer.h | 115 ++++ .../kdsharedmemorylocker.cpp | 40 ++ .../kdsharedmemorylocker.h | 36 + .../kdsingleapplicationguard.cpp | 622 ++++++++++++++++++ .../kdsingleapplicationguard.h | 74 +++ .../kdtoolsglobal.cpp | 32 + .../kdsingleapplicationguard/kdtoolsglobal.h | 113 ++++ .../kdsingleapplicationguard/license-gpl | 349 ++++++++++ .../kdsingleapplicationguard/pimpl_ptr.cpp | 203 ++++++ .../kdsingleapplicationguard/pimpl_ptr.h | 44 ++ src/libtomahawk/qtsingleapp/qtlocalpeer.cpp | 199 ------ src/libtomahawk/qtsingleapp/qtlocalpeer.h | 72 -- src/libtomahawk/qtsingleapp/qtlockedfile.cpp | 192 ------ src/libtomahawk/qtsingleapp/qtlockedfile.h | 96 --- .../qtsingleapp/qtlockedfile_unix.cpp | 114 ---- .../qtsingleapp/qtlockedfile_win.cpp | 208 ------ .../qtsingleapp/qtsingleapplication.cpp | 344 ---------- .../qtsingleapp/qtsingleapplication.h | 84 --- .../qtsingleapp/qtsinglecoreapplication.cpp | 148 ----- .../qtsingleapp/qtsinglecoreapplication.h | 66 -- src/main.cpp | 26 +- src/tomahawkapp.cpp | 18 +- 26 files changed, 2137 insertions(+), 1558 deletions(-) create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp create mode 100644 src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h create mode 100644 src/libtomahawk/kdsingleapplicationguard/license-gpl create mode 100644 src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp create mode 100644 src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h delete mode 100644 src/libtomahawk/qtsingleapp/qtlocalpeer.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtlocalpeer.h delete mode 100644 src/libtomahawk/qtsingleapp/qtlockedfile.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtlockedfile.h delete mode 100644 src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtsingleapplication.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtsingleapplication.h delete mode 100644 src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp delete mode 100644 src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h diff --git a/include/tomahawk/tomahawkapp.h b/include/tomahawk/tomahawkapp.h index bf83f6e86..7775a6528 100644 --- a/include/tomahawk/tomahawkapp.h +++ b/include/tomahawk/tomahawkapp.h @@ -40,6 +40,7 @@ #include "network/servent.h" #include "utils/tomahawkutils.h" +#include "kdsingleapplicationguard/kdsingleapplicationguard.h" class AudioEngine; class Database; @@ -98,9 +99,11 @@ public: // because QApplication::arguments() is expensive bool scrubFriendlyName() const { return m_scrubFriendlyName; } +public slots: + void instanceStarted( KDSingleApplicationGuard::Instance ); + private slots: void setupSIP(); - void messageReceived( const QString& ); private: void initLocalCollection(); diff --git a/src/headlesscheck.h b/src/headlesscheck.h index 189f1127e..f82fcea33 100644 --- a/src/headlesscheck.h +++ b/src/headlesscheck.h @@ -21,14 +21,14 @@ #ifdef ENABLE_HEADLESS -#define TOMAHAWK_APPLICATION QtSingleCoreApplication +#define TOMAHAWK_APPLICATION QCoreApplication #define TOMAHAWK_HEADLESS -#include "qtsingleapp/qtsingleapplication.h" +#include > #else -#define TOMAHAWK_APPLICATION QtSingleApplication -#include "qtsingleapp/qtsingleapplication.h" +#define TOMAHAWK_APPLICATION QApplication +#include #include "tomahawkwindow.h" #endif diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index b7f214428..c7f3ac4e2 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -146,8 +146,10 @@ set( libSources widgets/overlaywidget.cpp widgets/infowidgets/sourceinfowidget.cpp - qtsingleapp/qtlocalpeer.cpp - qtsingleapp/qtsingleapplication.cpp + kdsingleapplicationguard/kdsingleapplicationguard.cpp + kdsingleapplicationguard/kdsharedmemorylocker.cpp + kdsingleapplicationguard/kdtoolsglobal.cpp + kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp ) set( libHeaders @@ -289,8 +291,10 @@ set( libHeaders widgets/overlaywidget.h widgets/infowidgets/sourceinfowidget.h - qtsingleapp/qtlocalpeer.h - qtsingleapp/qtsingleapplication.h + kdsingleapplicationguard/kdsingleapplicationguard.h + kdsingleapplicationguard/kdsharedmemorylocker.h + kdsingleapplicationguard/kdtoolsglobal.h + kdsingleapplicationguard/kdlockedsharedmemorypointer.h ) set( libHeaders_NoMOC diff --git a/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp new file mode 100644 index 000000000..e1fe10a4d --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp @@ -0,0 +1,475 @@ +#include "kdlockedsharedmemorypointer.h" + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) +#ifndef QT_NO_SHAREDMEMORY + +namespace kdtools +{ +} +using namespace kdtools; + +KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory * m ) + : locker( m ), + mem( m ) +{ + +} + +KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory & m ) + : locker( &m ), + mem( &m ) +{ + +} + +KDLockedSharedMemoryPointerBase::~KDLockedSharedMemoryPointerBase() {} + +void * KDLockedSharedMemoryPointerBase::get() { + return mem ? mem->data() : 0 ; +} + +const void * KDLockedSharedMemoryPointerBase::get() const { + return mem ? mem->data() : 0 ; +} + +size_t KDLockedSharedMemoryPointerBase::byteSize() const { + return mem->size(); +} + +/*! + \class KDLockedSharedMemoryPointer + \ingroup core raii smartptr + \brief Locking pointer for Qt shared memory segments + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + KDLockedSharedMemoryPointer is a smart immutable pointer, which gives convenient and safe access to a QSharedMemory data segment. + The content of a KDLockedSharedMemoryPointer cannot be changed during it's lifetime. + + You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it. + \note You can only put simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the + documentation of QSharedMemory for details. +*/ + +/*! + \fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory * mem ) + + Constructor. Constructs a KDLockedSharedMemory pointer which points to the data segment of \a mem. + The constructor locks \a mem. If the memory segment is already locked by another process, this constructor + blocks until the lock is released. + + \post data() == mem->data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory & mem ) + + \overload + + \post data() == mem.data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryPointer::~KDLockedSharedMemoryPointer() + + Destructor. Unlocks the shared memory segment. + + \post The shared memory segment has been unlocked +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::get() + + \returns a pointer to the contained object. +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::get() const + + \returns a const pointer to the contained object + \overload +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::data() + + Equivalent to get(), provided for consistency with Qt naming conventions. +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::data() const + + \overload +*/ + +/*! + \fn T & KDLockedSharedMemoryPointer::operator*() + + Dereference operator. Returns \link get() *get()\endlink. +*/ + +/*! + \fn const T & KDLockedSharedMemoryPointer::operator*() const + + Dereference operator. Returns \link get() *get()\endlink. + \overload +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::operator->() + + Member-by-pointer operator. Returns get(). +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::operator->() const + + Member-by-pointer operator. Returns get(). + \overload +*/ + +/*! + \class KDLockedSharedMemoryArray + \ingroup core raii smartptr + \brief Locking array pointer to Qt shared memory segments + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + KDLockedSharedMemoryArray is a smart immutable pointer, which gives convenient and safe access to array data stored in a QSharedMemory + data segment. + The content of a KDLockedSharedMemoryArray cannot be changed during it's lifetime. + + You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it. + \note You can only put arrays of simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the + documentation of QSharedMemory for details. + + \sa KDLockedSharedMemoryPointer +*/ + +/*! + \fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory* mem ) + Constructor. Constructs a KDLockedSharedMemoryArray which points to the data segment of \a mem. The constructor locks \a mem. If the memory + segment is already locked by another process, this constructor blocks until the lock is release. + + \post get() == mem->data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory& mem ) + \overload + + \post get() == mem->data() and the memory segment has been locked +*/ + + +/*! + \typedef KDLockedSharedMemoryArray::size_type + Typedef for std::size_t. Provided for STL compatibility. +*/ + +/*! + \typedef KDLockedSharedMemoryArray::difference_type + Typedef for std::ptrdiff_t. Provided for STL compatibility. +*/ + +/*! + \typedef KDLockedSharedMemoryArray::iterator + Typedef for T*. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::const_iterator + Typedef for const T*. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::reverse_iterator + Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::iterator iterator\endlink >. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::const_reverse_iterator + Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::const_iterator const_iterator\endlink >. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::begin() + Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the first item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::begin() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::end() + Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the item after the last item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::end() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rbegin() + Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the item after the last item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rbegin() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rend() + Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the first item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rend() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::size_type KDLockedSharedMemoryArray::size() const + Returns the size of this array. The size is calculated from the storage size of T and + the size of the shared memory segment. + \since_f 2.2 +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::operator[]( difference_type n ) + Array access operator. Returns a reference to the item at index position \a n. +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::operator[]( difference_type n ) const + \overload +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::front() + Returns a reference to the first item in the array. This is the same as operator[](0). +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::front() const + \overload +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::back() + Returns a reference to the last item in the array. This is the same as operator[](size()-1). + \since_f 2.2 +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::back() const + \overload + \since_f 2.2 +*/ + + +#ifdef eKDTOOLSCORE_UNITTESTS + +#include + +#include +#include + +namespace +{ + struct TestStruct + { + TestStruct( uint nn = 0 ) + : n( nn ), + f( 0.0 ), + c( '\0' ), + b( false ) + { + } + uint n; + double f; + char c; + bool b; + }; + + bool operator==( const TestStruct& lhs, const TestStruct& rhs ) + { + return lhs.n == rhs.n && lhs.f == rhs.f && lhs.c == rhs.c && lhs.b == rhs.b; + } + + class TestThread : public QThread + { + public: + TestThread( const QString& key ) + : mem( key ) + { + mem.attach(); + } + + void run() + { + while( true ) + { + msleep( 100 ); + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + if( !p->b ) + continue; + + p->n = 5; + p->f = 3.14; + p->c = 'A'; + p->b = false; + return; + } + } + + QSharedMemory mem; + }; + + bool isConst( TestStruct* ) + { + return false; + } + + bool isConst( const TestStruct* ) + { + return true; + } +} + + +KDAB_UNITTEST_SIMPLE( KDLockedSharedMemoryPointer, "kdcoretools" ) { + + const QString key = QUuid::createUuid(); + QSharedMemory mem( key ); + const bool created = mem.create( sizeof( TestStruct ) ); + assertTrue( created ); + if ( !created ) + return; // don't execute tests if shm coulnd't be created + + // On Windows, shared mem is only available in increments of page + // size (4k), so don't fail if the segment is larger: + const unsigned long mem_size = mem.size(); + assertGreaterOrEqual( mem_size, sizeof( TestStruct ) ); + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertTrue( p ); + *p = TestStruct(); + assertEqual( p->n, 0u ); + assertEqual( p->f, 0.0 ); + assertEqual( p->c, '\0' ); + assertFalse( p->b ); + } + + { + TestThread thread( key ); + assertEqual( thread.mem.key().toStdString(), key.toStdString() ); + assertEqual( static_cast< unsigned long >( thread.mem.size() ), mem_size ); + thread.start(); + + assertTrue( thread.isRunning() ); + thread.wait( 2000 ); + assertTrue( thread.isRunning() ); + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + p->b = true; + } + + thread.wait( 2000 ); + assertFalse( thread.isRunning() ); + } + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertEqual( p->n, 5u ); + assertEqual( p->f, 3.14 ); + assertEqual( p->c, 'A' ); + assertFalse( p->b ); + } + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( mem ); + assertEqual( mem.data(), p.get() ); + assertEqual( p.get(), p.operator->() ); + assertEqual( p.get(), &(*p) ); + assertEqual( p.get(), p.data() ); + assertFalse( isConst( p.get() ) ); + } + + { + const kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertEqual( mem.data(), p.get() ); + assertEqual( p.get(), p.operator->() ); + assertEqual( p.get(), &(*p) ); + assertEqual( p.get(), p.data() ); + assertTrue( isConst( p.get() ) ); + } + + { + QSharedMemory mem2( key + key ); + const bool created2 = mem2.create( 16 * sizeof( TestStruct ) ); + assertTrue( created2 ); + if ( !created2 ) + return; // don't execute tests if shm coulnd't be created + + kdtools::KDLockedSharedMemoryArray a( mem2 ); + assertTrue( a ); + assertEqual( a.get(), mem2.data() ); + assertEqual( &a[0], a.get() ); + + a[1] = a[0]; + assertTrue( a[0] == a[1] ); + + TestStruct ts; + ts.n = 5; + ts.f = 3.14; + a[0] = ts; + assertFalse( a[0] == a[1] ); + assertEqual( a.front().n, ts.n ); + assertEqual( a[0].f, ts.f ); + a[0].n = 10; + assertEqual( a.front().n, 10u ); + ts = a[0]; + assertEqual( ts.n, 10u ); + + std::vector< TestStruct > v; + for( uint i = 0; i < a.size(); ++i ) + v.push_back( TestStruct( i ) ); + + std::copy( v.begin(), v.end(), a.begin() ); + for( uint i = 0; i < a.size(); ++i ) + assertEqual( a[ i ].n, i ); + assertEqual( a.front().n, 0u ); + assertEqual( a.back().n, a.size() - 1 ); + + std::copy( v.begin(), v.end(), a.rbegin() ); + for( uint i = 0; i < a.size(); ++i ) + assertEqual( a[ i ].n, a.size() - 1 - i ); + assertEqual( a.front().n, a.size() - 1 ); + assertEqual( a.back().n, 0u ); + } + +} +#endif // KDTOOLSCORE_UNITTESTS +#endif // QT_NO_SHAREDMEMORY +#endif // QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) diff --git a/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h new file mode 100644 index 000000000..df0ea4998 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h @@ -0,0 +1,115 @@ +#ifndef __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ +#define __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ + +#include + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) +#ifndef QT_NO_SHAREDMEMORY + +#include "kdsharedmemorylocker.h" +#include + +#include + +#ifndef DOXYGEN_RUN +namespace kdtools { +#endif + +class KDLockedSharedMemoryPointerBase { +protected: + explicit KDLockedSharedMemoryPointerBase( QSharedMemory * mem ); + explicit KDLockedSharedMemoryPointerBase( QSharedMemory & mem ); + ~KDLockedSharedMemoryPointerBase(); + + // PENDING(marc) do we really want const propagation here? I + // usually declare all my RAII objects const... + void * get(); + const void * get() const; + + KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() ) + + size_t byteSize() const; + +private: + KDSharedMemoryLocker locker; + QSharedMemory * const mem; +}; + +template< typename T> +class MAKEINCLUDES_EXPORT KDLockedSharedMemoryPointer : KDLockedSharedMemoryPointerBase { + KDAB_DISABLE_COPY( KDLockedSharedMemoryPointer ); +public: + explicit KDLockedSharedMemoryPointer( QSharedMemory * m ) + : KDLockedSharedMemoryPointerBase( m ) {} + explicit KDLockedSharedMemoryPointer( QSharedMemory & m ) + : KDLockedSharedMemoryPointerBase( m ) {} + + T * get() { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + const T * get() const { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + + T * data() { return static_cast( get() ); } + const T * data() const { return static_cast( get() ); } + + T & operator*() { assert( get() ); return *get(); } + const T & operator*() const { assert( get() ); return *get(); } + + T * operator->() { return get(); } + const T * operator->() const { return get(); } + + KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase ) +}; + +template +class MAKEINCLUDES_EXPORT KDLockedSharedMemoryArray : KDLockedSharedMemoryPointerBase { + KDAB_DISABLE_COPY( KDLockedSharedMemoryArray ); +public: + explicit KDLockedSharedMemoryArray( QSharedMemory * m ) + : KDLockedSharedMemoryPointerBase( m ) {} + explicit KDLockedSharedMemoryArray( QSharedMemory & m ) + : KDLockedSharedMemoryPointerBase( m ) {} + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* iterator; + typedef const T* const_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + typedef std::reverse_iterator< iterator > reverse_iterator; + + iterator begin() { return get(); } + const_iterator begin() const { return get(); } + + iterator end() { return begin() + size(); } + const_iterator end() const { return begin() + size(); } + + reverse_iterator rbegin() { return reverse_iterator( end() ); } + const_reverse_iterator rbegin() const { return reverse_iterator( end() ); } + + reverse_iterator rend() { return reverse_iterator( begin() ); } + const_reverse_iterator rend() const { return const_reverse_iterator( begin() ); } + + size_type size() const { return byteSize() / sizeof( T ); } + + T * get() { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + const T * get() const { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + + T & operator[]( difference_type n ) { assert( get() ); return *(get()+n); } + const T & operator[]( difference_type n ) const { assert( get() ); return *(get()+n); } + + T & front() { assert( get() ); return *get(); } + const T & front() const { assert( get() ); return *get(); } + + T & back() { assert( get() ); return *( get() + size() - 1 ); } + const T & back() const { assert( get() ); return *( get() + size() - 1 ); } + + KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase ) +}; + +#ifndef DOXYGEN_RUN +} +#endif + +#endif /* QT_NO_SHAREDMEMORY */ + +#endif /* QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) */ + +#endif /* __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ */ diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp new file mode 100644 index 000000000..0c99b8fff --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp @@ -0,0 +1,40 @@ +#include "kdsharedmemorylocker.h" + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) + +#include + +using namespace kdtools; + +/*! + \class KDSharedMemoryLocker + \ingroup raii core + \brief Exception-safe and convenient wrapper around QSharedMemory::lock() +*/ + +/** + * Constructor. Locks the shared memory segment \a mem. + * If another process has locking the segment, this constructor blocks + * until the lock is released. The memory segments needs to be properly created or attached. + */ +KDSharedMemoryLocker::KDSharedMemoryLocker( QSharedMemory* mem ) + : mem( mem ) +{ + mem->lock(); +} + +/** + * Destructor. Unlocks the shared memory segment associated with this + * KDSharedMemoryLocker. + */ +KDSharedMemoryLocker::~KDSharedMemoryLocker() +{ + mem->unlock(); +} + +#ifdef KDAB_EVAL +#include KDAB_EVAL +static const EvalDialogChecker evalChecker( "KD Tools", false ); +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h new file mode 100644 index 000000000..7ae83e771 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h @@ -0,0 +1,36 @@ +#ifndef __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H +#define __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H + +#include "kdtoolsglobal.h" + +#if QT_VERSION < 0x040400 && !defined( DOXYGEN_RUN ) +#ifdef Q_CC_GNU +#warning "Can't use KDTools KDSharedMemoryLocker with Qt versions prior to 4.4" +#endif +#else + +class QSharedMemory; + +#ifndef DOXYGEN_RUN +namespace kdtools +{ +#endif + +class KDTOOLSCORE_EXPORT KDSharedMemoryLocker +{ + Q_DISABLE_COPY( KDSharedMemoryLocker ) +public: + KDSharedMemoryLocker( QSharedMemory* mem ); + ~KDSharedMemoryLocker(); + +private: + QSharedMemory* const mem; +}; + +#ifndef DOXYGEN_RUN +} +#endif + +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp new file mode 100644 index 000000000..63893dbde --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp @@ -0,0 +1,622 @@ +#include "kdsingleapplicationguard.h" + +#ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES +#define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 128 +#endif + +#ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE +#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 1024 +#endif + + + +KDSingleApplicationGuard::Instance::Instance( const QStringList& args, qint64 p ) + : arguments( args ), + pid( p ) +{ +} + +#if QT_VERSION < 0x040400 + +class KDSingleApplicationGuard::Private +{ +}; + +KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication*, Policy ) +{ + qWarning( "KD Tools was compiled with a Qt version prior to 4.4. SingleApplicationGuard won't work." ); +} + +KDSingleApplicationGuard::~KDSingleApplicationGuard() +{ +} + +void KDSingleApplicationGuard::shutdownOtherInstances() +{ +} + +void KDSingleApplicationGuard::killOtherInstances() +{ +} + +void KDSingleApplicationGuard::timerEvent( QTimerEvent* ) +{ +} +#else + +#include +#include + +#include "kdsharedmemorylocker.h" +#include "kdlockedsharedmemorypointer.h" + +#include +#include +#include + +#ifndef Q_WS_WIN +#include +#endif + +using namespace kdtools; + +/*! + \class KDSingleApplicationGuard KDSingleApplicationGuard + \brief A guard to protect an application from having several instances. + + KDSingleApplicationGuard can be used to make sure only one instance of an + application is running at the same time. + + \note As KDSingleApplicationGuard uses QSharedMemory Qt 4.4 or later is required + */ + +/*! + \fn void KDSingleApplicationGuard::instanceStarted() + This signal is emitted by the primary instance when ever one other + instance was started. + */ + +/*! + \fn void KDSingleApplicationGuard::instanceExited() + This signal is emitted by the primary instance when ever one other + instance was exited. + */ + +/*! + \fn void KDSingleApplicationGuard::becamePrimaryInstance() + This signal is emitted, when the current running application gets the new + primary application. The old primary application has quit. + */ + +enum Command +{ + NoCommand = 0x00, + ExitedInstance = 0x01, + NewInstance = 0x02, + FreeInstance = 0x04, + ShutDownCommand = 0x08, + KillCommand = 0x10, + BecomePrimaryCommand = 0x20 +}; + +Q_DECLARE_FLAGS( Commands, Command ) +Q_DECLARE_OPERATORS_FOR_FLAGS( Commands ) + +struct ProcessInfo +{ + explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 ) + : command( c ), + pid( p ) + { + std::fill_n( commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, '\0' ); + + int argpos = 0; + for( QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it ) + { + const QByteArray arg = it->toLatin1(); + const int count = qMin( KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos, arg.count() ); + std::copy( arg.begin(), arg.begin() + count, commandline + argpos ); + argpos += arg.count() + 1; // makes sure there's a \0 between every parameter + } + } + + QStringList arguments() const + { + QStringList result; + + QByteArray arg; + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE; ++i ) + { + if( commandline[ i ] == '\0' && !arg.isEmpty() ) + { + result.push_back( QString::fromLatin1( arg ) ); + arg.clear(); + } + else if( !commandline[ i ] == '\0' ) + { + arg.push_back( commandline[ i ] ); + } + } + + return result; + } + + Commands command; + char commandline[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ]; + qint64 pid; +}; + +bool operator==( const ProcessInfo& lhs, const ProcessInfo& rhs ) +{ + return lhs.command == rhs.command && + ::memcmp( lhs.commandline, rhs.commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ) == 0; +} + +bool operator!=( const ProcessInfo& lhs, const ProcessInfo& rhs ) +{ + return !operator==( lhs, rhs ); +} + +/*! + This struct contains information about the managed process system. + \internal + */ +struct InstanceRegister +{ + InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy ) + : policy( policy ) + { + std::fill_n( info, KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ); + ::memcpy( magicCookie, "kdsingleapp", 12 ); + } + + /*! + Returns wheter this register was properly initialized by the first instance. + */ + bool isValid() const + { + return ::strcmp( magicCookie, "kdsingleapp" ) == 0; + } + + ProcessInfo info[ KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ]; + KDSingleApplicationGuard::Policy policy; + char magicCookie[ 12 ]; +}; + +bool operator==( const InstanceRegister& lhs, const InstanceRegister& rhs ) +{ + if( lhs.policy != rhs.policy ) + return false; + + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + if( lhs.info[ i ] != rhs.info[ i ] ) + return false; + + return true; +} + +/*! + \internal + */ +class KDSingleApplicationGuard::Private +{ +public: + Private( KDSingleApplicationGuard* qq ) + : q( qq ), + id( -1 ) + { + if( primaryInstance == 0 ) + primaryInstance = q; + } + + ~Private() + { + if( primaryInstance == q ) + primaryInstance = 0; + } + + void shutdownInstance() + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem ); + instances->info[ q->d->id ].command = ExitedInstance; + + if( q->isPrimaryInstance() ) + { + // ohh... we need a new primary instance... + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 ) + { + instances->info[ i ].command |= BecomePrimaryCommand; + return; + } + } + // none found? then my species is dead :-( + } + } + + static KDSingleApplicationGuard* primaryInstance; + +private: + KDSingleApplicationGuard* const q; + +public: + Policy policy; + QSharedMemory mem; + int id; +}; + +KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0; + +#ifndef Q_WS_WIN +void SIGINT_handler( int sig ) +{ + if( sig == SIGINT && KDSingleApplicationGuard::Private::primaryInstance != 0 ) + KDSingleApplicationGuard::Private::primaryInstance->d->shutdownInstance(); + ::exit( 1 ); +} +#endif + +/*! + Creates a new KDSingleApplicationGuard guarding \a parent from mulitply instances. + If \a policy is AutoKillOtherInstances (the default), all instances, which try to start, + are killed automatically and instanceStarted() is emitted. + If \a policy is NoPolicy, the other instance will run and instanceStarted() is emitted. + */ +KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication* parent, Policy policy ) + : QObject( parent ), + d( new Private( this ) ) +{ + const QString name = parent->applicationName(); + Q_ASSERT_X( !name.isEmpty(), "KDSingleApplicationGuard::KDSingleApplicationGuard", "applicationName must not be emty" ); + d->mem.setKey( name ); + + // if another instance crashed, the shared memory segment is still there on Unix + // the following lines trigger deletion in that case +#ifndef Q_WS_WIN + d->mem.attach(); + d->mem.detach(); +#endif + + d->policy = policy; + + const bool created = d->mem.create( sizeof( InstanceRegister ) ); + if( !created ) + { + if( !d->mem.attach() ) + { + qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." ); + return; + } + + // lets wait till the other instance initialized the register + bool initialized = false; + while( !initialized ) + { + const KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + initialized = instances->isValid(); + } + } + + + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + if( !created ) + { + // we're _not_ the first instance + // but the + bool killOurSelf = false; + + // find a new slot... + d->id = std::find( instances->info, instances->info + KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ) - instances->info; + ProcessInfo& info = instances->info[ d->id ]; + info = ProcessInfo( NewInstance, parent->arguments(), QCoreApplication::applicationPid() ); + killOurSelf = instances->policy == AutoKillOtherInstances; + d->policy = instances->policy; + + // but the signal that we tried to start was sent to the primary application + if( killOurSelf ) + { + info.command |= ExitedInstance; + exit( 1 ); + } + } + else + { + // ok.... we are the first instance + InstanceRegister reg( policy ); // create a new list + d->id = 0; // our id = 0 + // and we've no command + reg.info[ 0 ] = ProcessInfo( NoCommand, parent->arguments(), QCoreApplication::applicationPid() ); + *instances = reg; // push this is the process list into shared memory + } + +#ifndef Q_WS_WIN + ::signal( SIGINT, SIGINT_handler ); +#endif + + // now listen for commands + startTimer( 250 ); +} + +/*! + Destroys this SingleApplicationGuard. + If this instance has been the primary instance and no other instance is existing anymore, + the application is shut down completely. Otherwise the destructor selects another instance to + be the primary instances. + */ +KDSingleApplicationGuard::~KDSingleApplicationGuard() +{ + if( d->id == -1 ) + return; + + d->shutdownInstance(); +} + +/*! + \property KDSingleApplicationGuard::primaryInstance + Determines wheter this instance is the primary instance. + The primary instance is the first instance which was started or an instance which + got selected by KDSingleApplicationGuard's destructor, when the primary instance was + shut down. + + Get this property's value using %isPrimaryInstance(), and monitor changes to it + using becamePrimaryInstance(). + */ +bool KDSingleApplicationGuard::isPrimaryInstance() const +{ + return d->id == 0; +} + +/*! + \property KDSingleApplicationGuard::Policy + Specifies the policy KDSingleApplicationGuard is using when new instances are started. + This can only be set in the primary instance. + + Get this property's value using %policy(), set it using %setPolicy(), and monitor changes + to it using policyChanged(). + */ +KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const +{ + return d->policy; +} + +void KDSingleApplicationGuard::setPolicy( Policy policy ) +{ + Q_ASSERT( isPrimaryInstance() ); + if( d->policy == policy ) + return; + + d->policy = policy; + emit policyChanged(); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + instances->policy = policy; +} + +/*! + Returns a list of all currently running instances. + */ +QList< KDSingleApplicationGuard::Instance > KDSingleApplicationGuard::instances() const +{ + QList< Instance > result; + const KDLockedSharedMemoryPointer< InstanceRegister > instances( const_cast< QSharedMemory* >( &d->mem ) ); + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + const ProcessInfo& info = instances->info[ i ]; + if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 ) + result.push_back( Instance( info.arguments(), info.pid ) ); + } + return result; +} + +/*! + Shuts down all other instances. This can only be called from the + the primary instance. + Shut down is done gracefully via QCoreApplication::quit(). + */ +void KDSingleApplicationGuard::shutdownOtherInstances() +{ + Q_ASSERT( isPrimaryInstance() ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = ShutDownCommand; + } +} + +/*! + Kills all other instances. This can only be called from the + the primary instance. + Killing is done via exit(1) + */ +void KDSingleApplicationGuard::killOtherInstances() +{ + Q_ASSERT( isPrimaryInstance() ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = KillCommand; + } +} + +/*! + \reimp + */ +void KDSingleApplicationGuard::timerEvent( QTimerEvent* event ) +{ + Q_UNUSED( event ) + + if( isPrimaryInstance() ) + { + // only the primary instance will get notified about new instances + QList< Instance > exitedInstances; + QList< Instance > startedInstances; + + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + ProcessInfo& info = instances->info[ i ]; + if( info.command & NewInstance ) + { + startedInstances.push_back( Instance( info.arguments(), info.pid ) ); + info.command &= ~NewInstance; // clear NewInstance flag + } + else if( info.command & ExitedInstance ) + { + exitedInstances.push_back( Instance( info.arguments(), info.pid ) ); + info.command = FreeInstance; // set FreeInstance flag + } + } + } + + // one signal for every new instance - _after_ the memory segment was unlocked again + for( QList< Instance >::const_iterator it = startedInstances.begin(); it != startedInstances.end(); ++it ) + emit instanceStarted( *it ); + for( QList< Instance >::const_iterator it = exitedInstances.begin(); it != exitedInstances.end(); ++it ) + emit instanceExited( *it ); + } + else + { + // do we have a command? + bool killOurSelf = false; + bool shutDownOurSelf = false; + bool policyDidChange = false; + + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + policyDidChange = instances->policy != d->policy; + d->policy = instances->policy; + + if( instances->info[ d->id ].command & BecomePrimaryCommand ) + { + // we became primary! + instances->info[ 0 ] = instances->info[ d->id ]; + instances->info[ d->id ] = ProcessInfo(); // change our id to 0 and declare the old slot as free + d->id = 0; + emit becamePrimaryInstance(); + } + + killOurSelf = instances->info[ d->id ].command & KillCommand; // check for kill command + shutDownOurSelf = instances->info[ d->id ].command & ShutDownCommand; // check for shut down command + instances->info[ d->id ].command &= ~( KillCommand | ShutDownCommand | BecomePrimaryCommand ); // reset both flags + if( killOurSelf ) + { + instances->info[ d->id ].command |= ExitedInstance; // upon kill, we have to set the ExitedInstance flag + d->id = -1; // becauso our d'tor won't be called anymore + } + } + + if( killOurSelf ) // kill our self takes precedence + exit( 1 ); + else if( shutDownOurSelf ) + qApp->quit(); + else if( policyDidChange ) + emit policyChanged(); + } +} + +#ifdef KDTOOLSCORE_UNITTESTS + +#include + +#include + +#include +#include +#include + +Q_DECLARE_METATYPE( KDSingleApplicationGuard::Instance ); + +static void wait( int msec ) +{ + QTime t; + t.start(); + while( t.elapsed() < msec ) + { + qApp->processEvents( QEventLoop::WaitForMoreEvents, msec - t.elapsed() ); + } +} + +static std::ostream& operator<<( std::ostream& stream, const QStringList& list ) +{ + stream << "QStringList("; + for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it ) + { + stream << " " << it->toLocal8Bit().data(); + if( it + 1 != list.end() ) + stream << ","; + } + stream << " )"; + return stream; +} + + +KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) { + + // set it to an unique name + qApp->setApplicationName( QUuid::createUuid().toString() ); + + qRegisterMetaType< KDSingleApplicationGuard::Instance >(); + + KDSingleApplicationGuard* guard3 = 0; + QSignalSpy* spy3 = 0; + + { + KDSingleApplicationGuard guard1( qApp ); + assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances ); + assertEqual( guard1.instances().count(), 1 ); + assertTrue( guard1.isPrimaryInstance() ); + + guard1.setPolicy( KDSingleApplicationGuard::NoPolicy ); + assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy ); + + QSignalSpy spy1( &guard1, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + + KDSingleApplicationGuard guard2( qApp ); + assertEqual( guard1.instances().count(), 2 ); + assertEqual( guard2.instances().count(), 2 ); + assertEqual( guard2.policy(), KDSingleApplicationGuard::NoPolicy ); + assertFalse( guard2.isPrimaryInstance() ); + + wait( 1000 ); + + assertEqual( spy1.count(), 1 ); + guard3 = new KDSingleApplicationGuard( qApp ); + spy3 = new QSignalSpy( guard3, SIGNAL( becamePrimaryInstance() ) ); + assertFalse( guard3->isPrimaryInstance() ); + } + + wait( 1000 ); + assertEqual( spy3->count(), 1 ); + assertEqual( guard3->instances().count(), 1 ); + assertTrue( guard3->isPrimaryInstance() ); + + assertEqual( guard3->instances().first().arguments, qApp->arguments() ); + + QSignalSpy spyStarted( guard3, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + QSignalSpy spyExited( guard3, SIGNAL( instanceExited( KDSingleApplicationGuard::Instance ) ) ); + + { + KDSingleApplicationGuard guard1( qApp ); + KDSingleApplicationGuard guard2( qApp ); + + wait( 1000 ); + + assertEqual( spyStarted.count(), 2 ); + } + + wait( 1000 ); + assertEqual( spyExited.count(), 2 ); + + delete spy3; + delete guard3; + } + +#endif // KDTOOLSCORE_UNITTESTS + +#endif // QT_VERSION < 0x040400 diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h new file mode 100644 index 000000000..f649ef836 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h @@ -0,0 +1,74 @@ +#ifndef __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ +#define __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ + +#include +#include + +#include "pimpl_ptr.h" + +class QCoreApplication; + +#ifndef Q_WS_WIN +void SIGINT_handler( int sig ); +#endif + +class KDTOOLSCORE_EXPORT KDSingleApplicationGuard : public QObject +{ + Q_OBJECT +#ifndef Q_WS_WIN + friend void ::SIGINT_handler( int ); +#endif + +public: + enum Policy + { + NoPolicy = 0, + AutoKillOtherInstances = 1 + }; + + Q_PROPERTY( bool primaryInstance READ isPrimaryInstance NOTIFY becamePrimaryInstance ) + Q_PROPERTY( Policy policy READ policy WRITE setPolicy NOTIFY policyChanged ) + + explicit KDSingleApplicationGuard( QCoreApplication* parent, Policy policy = AutoKillOtherInstances ); + ~KDSingleApplicationGuard(); + + bool isPrimaryInstance() const; + + Policy policy() const; + void setPolicy( Policy policy ); + + struct Instance + { + Instance( const QStringList& arguments = QStringList(), qint64 pid = -1 ); + + QStringList arguments; + qint64 pid; + }; + + QList< Instance > instances() const; + +Q_SIGNALS: + void instanceStarted( KDSingleApplicationGuard::Instance instance ); + void instanceExited( KDSingleApplicationGuard::Instance instance ); + void becamePrimaryInstance(); + void policyChanged(); + +public Q_SLOTS: + void shutdownOtherInstances(); + void killOtherInstances(); + +protected: + void timerEvent( QTimerEvent* event ); + +private: + class Private; + kdtools::pimpl_ptr< Private > d; +}; + +#if QT_VERSION < 0x040400 +#ifdef Q_CC_GNU +#warning "Can't use KDSingleApplicationGuard with Qt versions prior to 4.4" +#endif +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp new file mode 100644 index 000000000..5997fe64c --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp @@ -0,0 +1,32 @@ +#include "kdtoolsglobal.h" + +#include + +#include + +namespace { + struct Version { + unsigned char v[3]; + }; + + static inline bool operator<( const Version & lhs, const Version & rhs ) { + return std::lexicographical_compare( lhs.v, lhs.v + 3, rhs.v, rhs.v + 3 ); + } + static inline bool operator==( const Version & lhs, const Version & rhs ) { + return std::equal( lhs.v, lhs.v + 3, rhs.v ); + } + KDTOOLS_MAKE_RELATION_OPERATORS( Version, static inline ) +} + +static Version kdParseQtVersion( const char * const version ) { + if ( !version || qstrlen( version ) < 5 || version[1] != '.' || version[3] != '.' || version[5] != 0 && version[5] != '.' && version[5] != '-' ) + return Version(); // parse error + const Version result = { { version[0] - '0', version[2] - '0', version[4] - '0' } }; + return result; +} + +bool _kdCheckQtVersion_impl( int major, int minor, int patchlevel ) { + static const Version actual = kdParseQtVersion( qVersion() ); // do this only once each run... + const Version requested = { { major, minor, patchlevel } }; + return actual >= requested; +} diff --git a/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h new file mode 100644 index 000000000..4e8d0d673 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h @@ -0,0 +1,113 @@ +#ifndef __KDTOOLS_KDTOOLSGLOBAL_H__ +#define __KDTOOLS_KDTOOLSGLOBAL_H__ + +#include + +#define KDAB_DISABLE_COPY( x ) private: x( const x & ); x & operator=( const x & ) + +#ifdef KDTOOLS_SHARED +# ifdef BUILD_SHARED_KDTOOLSCORE +# define KDTOOLSCORE_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSCORE_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDTOOLSGUI +# define KDTOOLSGUI_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSGUI_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDTOOLSXML +# define KDTOOLSXML_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSXML_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDUPDATER +# define KDTOOLS_UPDATER_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLS_UPDATER_EXPORT Q_DECL_IMPORT +# endif +#else // KDTOOLS_SHARED +# define KDTOOLSCORE_EXPORT +# define KDTOOLSGUI_EXPORT +# define KDTOOLSXML_EXPORT +# define KDTOOLS_UPDATER_EXPORT +#endif // KDTOOLS_SHARED + +#define MAKEINCLUDES_EXPORT + +#define DOXYGEN_PROPERTY( x ) +#ifdef DOXYGEN_RUN +# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) operator unspecified_bool_type() const { return func; } +# define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) operator unspecified_bool_type() const; +#else +# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) \ + private: struct __safe_bool_dummy__ { void nonnull() {} }; \ + typedef void ( __safe_bool_dummy__::*unspecified_bool_type )(); \ + public: \ + operator unspecified_bool_type() const { \ + return ( func ) ? &__safe_bool_dummy__::nonnull : 0 ; \ + } +#define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) \ + using Class::operator Class::unspecified_bool_type; +#endif + +#define KDTOOLS_MAKE_RELATION_OPERATORS( Class, linkage ) \ + linkage bool operator>( const Class & lhs, const Class & rhs ) { \ + return operator<( rhs, lhs ); \ + } \ + linkage bool operator!=( const Class & lhs, const Class & rhs ) { \ + return !operator==( lhs, rhs ); \ + } \ + linkage bool operator<=( const Class & lhs, const Class & rhs ) { \ + return !operator>( lhs, rhs ); \ + } \ + linkage bool operator>=( const Class & lhs, const Class & rhs ) { \ + return !operator<( lhs, rhs ); \ + } + +template +inline T & __kdtools__dereference_for_methodcall( T & o ) { + return o; +} + +template +inline T & __kdtools__dereference_for_methodcall( T * o ) { + return *o; +} + +#define KDAB_SET_OBJECT_NAME( x ) __kdtools__dereference_for_methodcall( x ).setObjectName( QLatin1String( #x ) ) + +KDTOOLSCORE_EXPORT bool _kdCheckQtVersion_impl( int major, int minor=0, int patchlevel=0 ); +static inline bool kdCheckQtVersion( unsigned int major, unsigned int minor=0, unsigned int patchlevel=0 ) { + return (major<<16|minor<<8|patchlevel) <= static_cast(QT_VERSION) + || _kdCheckQtVersion_impl( major, minor, patchlevel ); +} + +#define KDTOOLS_DECLARE_PRIVATE_BASE( Class ) \ +protected: \ + class Private; \ + Private * d_func() { return _d; } \ + const Private * d_func() const { return _d; } \ + Class( Private * _d_, bool b ) : _d( _d_ ) { init(b); } \ +private: \ + void init(bool); \ +private: \ + Private * _d + +#define KDTOOLS_DECLARE_PRIVATE_DERIVED( Class, Base ) \ +protected: \ + class Private; \ + Private * d_func() { \ + return reinterpret_cast( Base::d_func() ); \ + } \ + const Private * d_func() const { \ + return reinterpret_cast( Base::d_func() ); \ + } \ + Class( Private * _d_, bool b ) \ + : Base( reinterpret_cast(_d_), b ) { init(b); } \ +private: \ + void init(bool) + + +#endif /* __KDTOOLS_KDTOOLSGLOBAL_H__ */ + diff --git a/src/libtomahawk/kdsingleapplicationguard/license-gpl b/src/libtomahawk/kdsingleapplicationguard/license-gpl new file mode 100644 index 000000000..332ed973c --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/license-gpl @@ -0,0 +1,349 @@ + + The KD Tools Library is Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. + + You may use, distribute and copy the KD Tools Library under the terms of + GNU General Public License version 2, which is displayed below. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +------------------------------------------------------------------------- diff --git a/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp new file mode 100644 index 000000000..3045ebc2a --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp @@ -0,0 +1,203 @@ +#include "pimpl_ptr.h" + +/*! + \class pimpl_ptr: + \ingroup core smartptr + \brief Owning pointer for private implementations + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + pimpl_ptr is a smart immutable pointer, which owns the contained object. Unlike other smart pointers, + it creates a standard constructed object when instanciated via the + \link pimpl_ptr() standard constructor\endlink. + Additionally, pimpl_ptr respects constness of the pointer object and returns \c const \c T* for + a const pimpl_ptr object. + + The content of a pimpl_ptr cannot be changed during it's lifetime. + + \section general-use General Use + + The general use case of pimpl_ptr is the "Pimpl Idiom", i.e. hiding the private implementation of a class + from the user's compiler which see \c MyClass as + + \code + class MyClass + { + public: + MyClass(); + ~MyClass(); + + // public class API + int value() const; + + private: + class Private; // defined later + kdtools::pimpl_ptr< Private > d; + }; + \endcode + + but not the private parts of it. These can only be seen (and accessed) by the code knowing \c MyClass::Private: + + \code + class MyClass::Private + { + public: + int value; + }; + + MyClass::MyClass() + { + // d was automatically filled with new Private + d->value = 42; + } + + MyClass::~MyClass() + { + // the content of d gets deleted automatically + } + + int MyClass::value() const + { + // access the private part: + // since MyClass::value() is const, the returned pointee is const, too + return d->value; + } + \endcode + +*/ + +/*! + \fn pimpl_ptr::pimpl_ptr() + + Default constructor. Constructs a pimpl_tr that contains (owns) a standard constructed + instance of \c T. + + \post \c *this owns a new object. +*/ + +/*! + \fn pimpl_ptr::pimpl_ptr( T * t ) + + Constructor. Constructs a pimpl_ptr that contains (owns) \a t. + + \post get() == obj +*/ + +/*! + \fn pimpl_ptr::~pimpl_ptr() + + Destructor. + + \post The object previously owned by \c *this has been deleted. +*/ + +/*! + \fn const T * pimpl_ptr::get() const + + \returns a const pointer to the contained (owned) object. + \overload +*/ + +/*! + \fn T * pimpl_ptr::get() + + \returns a pointer to the contained (owned) object. +*/ + +/*! + \fn const T & pimpl_ptr::operator*() const + + Dereference operator. Returns \link get() *get()\endlink. + \overload +*/ + +/*! + \fn T & pimpl_ptr::operator*() + + Dereference operator. Returns \link get() *get()\endlink. +*/ + +/*! + \fn const T * pimpl_ptr::operator->() const + + Member-by-pointer operator. Returns get(). + \overload +*/ + +/*! + \fn T * pimpl_ptr::operator->() + + Member-by-pointer operator. Returns get(). +*/ + +#ifdef KDTOOLSCORE_UNITTESTS + +#include + +#include +#include + +namespace +{ + struct ConstTester + { + bool isConst() + { + return false; + } + + bool isConst() const + { + return true; + } + }; +} + +KDAB_UNITTEST_SIMPLE( pimpl_ptr, "kdcoretools" ) { + + { + kdtools::pimpl_ptr< QObject > p; + assertNotNull( p.get() ); + assertNull( p->parent() ); + } + + + { + QPointer< QObject > o; + { + kdtools::pimpl_ptr< QObject > qobject( new QObject ); + o = qobject.get(); + assertEqual( o, qobject.operator->() ); + assertEqual( o, &(qobject.operator*()) ); + } + assertNull( o ); + } + + { + const kdtools::pimpl_ptr< QObject > qobject( new QObject ); + const QObject* o = qobject.get(); + assertEqual( o, qobject.operator->() ); + assertEqual( o, &(qobject.operator*()) ); + } + + { + kdtools::pimpl_ptr< QObject > o1; + assertTrue( o1 ); + kdtools::pimpl_ptr< QObject > o2( 0 ); + assertFalse( o2 ); + } + + { + const kdtools::pimpl_ptr< ConstTester > o1; + kdtools::pimpl_ptr< ConstTester > o2; + assertTrue( o1->isConst() ); + assertFalse( o2->isConst() ); + assertTrue( (*o1).isConst() ); + assertFalse( (*o2).isConst() ); + assertTrue( o1.get()->isConst() ); + assertFalse( o2.get()->isConst() ); + } +} + +#endif // KDTOOLSCORE_UNITTESTS diff --git a/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h new file mode 100644 index 000000000..7b7f36839 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h @@ -0,0 +1,44 @@ +#ifndef __KDTOOLSCORE__PIMPL_PTR_H__ +#define __KDTOOLSCORE__PIMPL_PTR_H__ + +#include "kdtoolsglobal.h" + +#ifndef DOXYGEN_RUN +namespace kdtools { +#endif + + template + class pimpl_ptr { + KDAB_DISABLE_COPY( pimpl_ptr ); + T * d; + public: + pimpl_ptr() : d( new T ) {} + explicit pimpl_ptr( T * t ) : d( t ) {} + ~pimpl_ptr() { delete d; d = 0; } + + T * get() { return d; } + const T * get() const { return d; } + + T * operator->() { return get(); } + const T * operator->() const { return get(); } + + T & operator*() { return *get(); } + const T & operator*() const { return *get(); } + + KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() ) + }; + + // these are not implemented, so's we can catch their use at + // link-time. Leaving them undeclared would open up a comparison + // via operator unspecified-bool-type(). + template + void operator==( const pimpl_ptr &, const pimpl_ptr & ); + template + void operator!=( const pimpl_ptr &, const pimpl_ptr & ); + +#ifndef DOXYGEN_RUN +} // namespace kdtools +#endif + +#endif /* __KDTOOLSCORE__PIMPL_PTR_H__ */ + diff --git a/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp b/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp deleted file mode 100644 index 382d182dc..000000000 --- a/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtlocalpeer.h" -#include -#include - -#if defined(Q_OS_WIN) -#include -#include -typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); -static PProcessIdToSessionId pProcessIdToSessionId = 0; -#endif -#if defined(Q_OS_UNIX) -#include -#endif - -namespace QtLP_Private { -#include "qtlockedfile.cpp" -#if defined(Q_OS_WIN) -#include "qtlockedfile_win.cpp" -#else -#include "qtlockedfile_unix.cpp" -#endif -} - -const char* QtLocalPeer::ack = "ack"; - -QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) - : QObject(parent), id(appId) -{ - QString prefix = id; - if (id.isEmpty()) { - id = QCoreApplication::applicationFilePath(); -#if defined(Q_OS_WIN) - id = id.toLower(); -#endif - prefix = id.section(QLatin1Char('/'), -1); - } - prefix.remove(QRegExp("[^a-zA-Z]")); - prefix.truncate(6); - - QByteArray idc = id.toUtf8(); - quint16 idNum = qChecksum(idc.constData(), idc.size()); - socketName = QLatin1String("qtsingleapp-") + prefix - + QLatin1Char('-') + QString::number(idNum, 16); - -#if defined(Q_OS_WIN) - if (!pProcessIdToSessionId) { - QLibrary lib("kernel32"); - pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); - } - if (pProcessIdToSessionId) { - DWORD sessionId = 0; - pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); - socketName += QLatin1Char('-') + QString::number(sessionId, 16); - } -#else - socketName += QLatin1Char('-') + QString::number(::getuid(), 16); -#endif - - server = new QLocalServer(this); - QString lockName = QDir(QDir::tempPath()).absolutePath() - + QLatin1Char('/') + socketName - + QLatin1String("-lockfile"); - lockFile.setFileName(lockName); - lockFile.open(QIODevice::ReadWrite); -} - - - -bool QtLocalPeer::isClient() -{ - if (lockFile.isLocked()) - return false; - - if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) - return true; - - bool res = server->listen(socketName); -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) - // ### Workaround - if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { - QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); - res = server->listen(socketName); - } -#endif - if (!res) - qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); - QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); - return false; -} - - -bool QtLocalPeer::sendMessage(const QString &message, int timeout) -{ - if (!isClient()) - return false; - - QLocalSocket socket; - bool connOk = false; - for(int i = 0; i < 2; i++) { - // Try twice, in case the other instance is just starting up - socket.connectToServer(socketName); - connOk = socket.waitForConnected(timeout/2); - if (connOk || i) - break; - int ms = 250; -#if defined(Q_OS_WIN) - Sleep(DWORD(ms)); -#else - struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; - nanosleep(&ts, NULL); -#endif - } - if (!connOk) - return false; - - QByteArray uMsg(message.toUtf8()); - QDataStream ds(&socket); - ds.writeBytes(uMsg.constData(), uMsg.size()); - bool res = socket.waitForBytesWritten(timeout); - if (res) { - res &= socket.waitForReadyRead(timeout); // wait for ack - if (res) - res &= (socket.read(qstrlen(ack)) == ack); - } - return res; -} - - -void QtLocalPeer::receiveConnection() -{ - QLocalSocket* socket = server->nextPendingConnection(); - if (!socket) - return; - - while (socket->bytesAvailable() < (int)sizeof(quint32)) - socket->waitForReadyRead(); - QDataStream ds(socket); - QByteArray uMsg; - quint32 remaining; - ds >> remaining; - uMsg.resize(remaining); - int got = 0; - char* uMsgBuf = uMsg.data(); - do { - got = ds.readRawData(uMsgBuf, remaining); - remaining -= got; - uMsgBuf += got; - } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); - if (got < 0) { - qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); - delete socket; - return; - } - QString message(QString::fromUtf8(uMsg)); - socket->write(ack, qstrlen(ack)); - socket->waitForBytesWritten(1000); - delete socket; - emit messageReceived(message); //### (might take a long time to return) -} diff --git a/src/libtomahawk/qtsingleapp/qtlocalpeer.h b/src/libtomahawk/qtsingleapp/qtlocalpeer.h deleted file mode 100644 index 869af2ac2..000000000 --- a/src/libtomahawk/qtsingleapp/qtlocalpeer.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include -#include -#include - -#include "qtlockedfile.h" - -class QtLocalPeer : public QObject -{ - Q_OBJECT - -public: - QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); - bool isClient(); - bool sendMessage(const QString &message, int timeout); - QString applicationId() const - { return id; } - -Q_SIGNALS: - void messageReceived(const QString &message); - -protected Q_SLOTS: - void receiveConnection(); - -protected: - QString id; - QString socketName; - QLocalServer* server; - QtLP_Private::QtLockedFile lockFile; - -private: - static const char* ack; -}; diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile.cpp deleted file mode 100644 index 3e73ba652..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include "qtlockedfile.h" - -/*! - \class QtLockedFile - - \brief The QtLockedFile class extends QFile with advisory locking - functions. - - A file may be locked in read or write mode. Multiple instances of - \e QtLockedFile, created in multiple processes running on the same - machine, may have a file locked in read mode. Exactly one instance - may have it locked in write mode. A read and a write lock cannot - exist simultaneously on the same file. - - The file locks are advisory. This means that nothing prevents - another process from manipulating a locked file using QFile or - file system functions offered by the OS. Serialization is only - guaranteed if all processes that access the file use - QLockedFile. Also, while holding a lock on a file, a process - must not open the same file again (through any API), or locks - can be unexpectedly lost. - - The lock provided by an instance of \e QtLockedFile is released - whenever the program terminates. This is true even when the - program crashes and no destructors are called. -*/ - -/*! \enum QtLockedFile::LockMode - - This enum describes the available lock modes. - - \value ReadLock A read lock. - \value WriteLock A write lock. - \value NoLock Neither a read lock nor a write lock. -*/ - -/*! - Constructs an unlocked \e QtLockedFile object. This constructor - behaves in the same way as \e QFile::QFile(). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile() - : QFile() -{ -#ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Constructs an unlocked QtLockedFile object with file \a name. This - constructor behaves in the same way as \e QFile::QFile(const - QString&). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile(const QString &name) - : QFile(name) -{ -#ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Opens the file in OpenMode \a mode. - - This is identical to QFile::open(), with the one exception that the - Truncate mode flag is disallowed. Truncation would conflict with the - advisory file locking, since the file would be modified before the - write lock is obtained. If truncation is required, use resize(0) - after obtaining the write lock. - - Returns true if successful; otherwise false. - - \sa QFile::open(), QFile::resize() -*/ -bool QtLockedFile::open(OpenMode mode) -{ - if (mode & QIODevice::Truncate) { - qWarning("QtLockedFile::open(): Truncate mode not allowed."); - return false; - } - return QFile::open(mode); -} - -/*! - Returns \e true if this object has a in read or write lock; - otherwise returns \e false. - - \sa lockMode() -*/ -bool QtLockedFile::isLocked() const -{ - return m_lock_mode != NoLock; -} - -/*! - Returns the type of lock currently held by this object, or \e - QtLockedFile::NoLock. - - \sa isLocked() -*/ -QtLockedFile::LockMode QtLockedFile::lockMode() const -{ - return m_lock_mode; -} - -/*! - \fn bool QtLockedFile::lock(LockMode mode, bool block = true) - - Obtains a lock of type \a mode. The file must be opened before it - can be locked. - - If \a block is true, this function will block until the lock is - aquired. If \a block is false, this function returns \e false - immediately if the lock cannot be aquired. - - If this object already has a lock of type \a mode, this function - returns \e true immediately. If this object has a lock of a - different type than \a mode, the lock is first released and then a - new lock is obtained. - - This function returns \e true if, after it executes, the file is - locked by this object, and \e false otherwise. - - \sa unlock(), isLocked(), lockMode() -*/ - -/*! - \fn bool QtLockedFile::unlock() - - Releases a lock. - - If the object has no lock, this function returns immediately. - - This function returns \e true if, after it executes, the file is - not locked by this object, and \e false otherwise. - - \sa lock(), isLocked(), lockMode() -*/ - -/*! - \fn QtLockedFile::~QtLockedFile() - - Destroys the \e QtLockedFile object. If any locks were held, they - are released. -*/ diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile.h b/src/libtomahawk/qtsingleapp/qtlockedfile.h deleted file mode 100644 index 07a42bffb..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#ifndef QTLOCKEDFILE_H -#define QTLOCKEDFILE_H - -#include -#ifdef Q_OS_WIN -#include -#endif - -#if defined(Q_WS_WIN) -# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) -# define QT_QTLOCKEDFILE_EXPORT -# elif defined(QT_QTLOCKEDFILE_IMPORT) -# if defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# endif -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) -# elif defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) -# endif -#else -# define QT_QTLOCKEDFILE_EXPORT -#endif - -namespace QtLP_Private { - -class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile -{ -public: - enum LockMode { NoLock = 0, ReadLock, WriteLock }; - - QtLockedFile(); - QtLockedFile(const QString &name); - ~QtLockedFile(); - - bool open(OpenMode mode); - - bool lock(LockMode mode, bool block = true); - bool unlock(); - bool isLocked() const; - LockMode lockMode() const; - -private: -#ifdef Q_OS_WIN - Qt::HANDLE wmutex; - Qt::HANDLE rmutex; - QVector rmutexes; - QString mutexname; - - Qt::HANDLE getMutexHandle(int idx, bool doCreate); - bool waitMutex(Qt::HANDLE mutex, bool doBlock); - -#endif - LockMode m_lock_mode; -}; -} -#endif diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp deleted file mode 100644 index 715c7d9b1..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include -#include -#include -#include - -#include "qtlockedfile.h" - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; - int cmd = block ? F_SETLKW : F_SETLK; - int ret = fcntl(handle(), cmd, &fl); - - if (ret == -1) { - if (errno != EINTR && errno != EAGAIN) - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - - m_lock_mode = mode; - return true; -} - - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = F_UNLCK; - int ret = fcntl(handle(), F_SETLKW, &fl); - - if (ret == -1) { - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - m_lock_mode = NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); -} - diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp deleted file mode 100644 index 8090470cd..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include "qtlockedfile.h" -#include -#include - -#define MUTEX_PREFIX "QtLockedFile mutex " -// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS -#define MAX_READERS MAXIMUM_WAIT_OBJECTS - -#define TCHAR WCHAR - -Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) -{ - if (mutexname.isEmpty()) { - QFileInfo fi(*this); - mutexname = QString::fromLatin1(MUTEX_PREFIX) - + fi.absoluteFilePath().toLower(); - } - QString mname(mutexname); - if (idx >= 0) - mname += QString::number(idx); - - Qt::HANDLE mutex; - if (doCreate) { - QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); - return 0; - } - } - else { - QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) - qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); - return 0; - } - } - return mutex; -} - -bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) -{ - Q_ASSERT(mutex); - DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); - switch (res) { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: - return true; - break; - case WAIT_TIMEOUT: - break; - default: - qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); - } - return false; -} - - - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - if (!wmutex && !(wmutex = getMutexHandle(-1, true))) - return false; - - if (!waitMutex(wmutex, block)) - return false; - - if (mode == ReadLock) { - int idx = 0; - for (; idx < MAX_READERS; idx++) { - rmutex = getMutexHandle(idx, false); - if (!rmutex || waitMutex(rmutex, false)) - break; - CloseHandle(rmutex); - } - bool ok = true; - if (idx >= MAX_READERS) { - qWarning("QtLockedFile::lock(): too many readers"); - rmutex = 0; - ok = false; - } - else if (!rmutex) { - rmutex = getMutexHandle(idx, true); - if (!rmutex || !waitMutex(rmutex, false)) - ok = false; - } - if (!ok && rmutex) { - CloseHandle(rmutex); - rmutex = 0; - } - ReleaseMutex(wmutex); - if (!ok) - return false; - } - else { - Q_ASSERT(rmutexes.isEmpty()); - for (int i = 0; i < MAX_READERS; i++) { - Qt::HANDLE mutex = getMutexHandle(i, false); - if (mutex) - rmutexes.append(mutex); - } - if (rmutexes.size()) { - DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), - TRUE, block ? INFINITE : 0); - if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { - if (res != WAIT_TIMEOUT) - qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); - m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky - unlock(); - return false; - } - } - } - - m_lock_mode = mode; - return true; -} - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - if (m_lock_mode == ReadLock) { - ReleaseMutex(rmutex); - CloseHandle(rmutex); - rmutex = 0; - } - else { - foreach(Qt::HANDLE mutex, rmutexes) { - ReleaseMutex(mutex); - CloseHandle(mutex); - } - rmutexes.clear(); - ReleaseMutex(wmutex); - } - - m_lock_mode = QtLockedFile::NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); - if (wmutex) - CloseHandle(wmutex); -} diff --git a/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp b/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp deleted file mode 100644 index 5a8f1b035..000000000 --- a/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtsingleapplication.h" -#include "qtlocalpeer.h" -#include - - -/*! - \class QtSingleApplication qtsingleapplication.h - \brief The QtSingleApplication class provides an API to detect and - communicate with running instances of an application. - - This class allows you to create applications where only one - instance should be running at a time. I.e., if the user tries to - launch another instance, the already running instance will be - activated instead. Another usecase is a client-server system, - where the first started instance will assume the role of server, - and the later instances will act as clients of that server. - - By default, the full path of the executable file is used to - determine whether two processes are instances of the same - application. You can also provide an explicit identifier string - that will be compared instead. - - The application should create the QtSingleApplication object early - in the startup phase, and call isRunning() to find out if another - instance of this application is already running. If isRunning() - returns false, it means that no other instance is running, and - this instance has assumed the role as the running instance. In - this case, the application should continue with the initialization - of the application user interface before entering the event loop - with exec(), as normal. - - The messageReceived() signal will be emitted when the running - application receives messages from another instance of the same - application. When a message is received it might be helpful to the - user to raise the application so that it becomes visible. To - facilitate this, QtSingleApplication provides the - setActivationWindow() function and the activateWindow() slot. - - If isRunning() returns true, another instance is already - running. It may be alerted to the fact that another instance has - started by using the sendMessage() function. Also data such as - startup parameters (e.g. the name of the file the user wanted this - new instance to open) can be passed to the running instance with - this function. Then, the application should terminate (or enter - client mode). - - If isRunning() returns true, but sendMessage() fails, that is an - indication that the running instance is frozen. - - Here's an example that shows how to convert an existing - application to use QtSingleApplication. It is very simple and does - not make use of all QtSingleApplication's functionality (see the - examples for that). - - \code - // Original - int main(int argc, char **argv) - { - QApplication app(argc, argv); - - MyMainWidget mmw; - mmw.show(); - return app.exec(); - } - - // Single instance - int main(int argc, char **argv) - { - QtSingleApplication app(argc, argv); - - if (app.isRunning()) - return !app.sendMessage(someDataString); - - MyMainWidget mmw; - app.setActivationWindow(&mmw); - mmw.show(); - return app.exec(); - } - \endcode - - Once this QtSingleApplication instance is destroyed (normally when - the process exits or crashes), when the user next attempts to run the - application this instance will not, of course, be encountered. The - next instance to call isRunning() or sendMessage() will assume the - role as the new running instance. - - For console (non-GUI) applications, QtSingleCoreApplication may be - used instead of this class, to avoid the dependency on the QtGui - library. - - \sa QtSingleCoreApplication -*/ - - -void QtSingleApplication::sysInit(const QString &appId) -{ - actWin = 0; - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a GUIenabled are passed on to the QAppliation constructor. - - If you are creating a console application (i.e. setting \a - GUIenabled to false), you may consider using - QtSingleCoreApplication instead. -*/ - -QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) - : QApplication(argc, argv, GUIenabled) -{ - sysInit(); -} - - -/*! - Creates a QtSingleApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QAppliation constructor. -*/ - -QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) - : QApplication(argc, argv) -{ - sysInit(appId); -} - - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a type are passed on to the QAppliation constructor. -*/ -QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) - : QApplication(argc, argv, type) -{ - sysInit(); -} - - -#if defined(Q_WS_X11) -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, - and \a cmap are passed on to the QApplication constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be \a appId. \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(appId); -} -#endif - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ -bool QtSingleApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ -QString QtSingleApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - Sets the activation window of this application to \a aw. The - activation window is the widget that will be activated by - activateWindow(). This is typically the application's main window. - - If \a activateOnMessage is true (the default), the window will be - activated automatically every time a message is received, just prior - to the messageReceived() signal being emitted. - - \sa activateWindow(), messageReceived() -*/ - -void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) -{ - actWin = aw; - if (activateOnMessage) - connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); - else - disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); -} - - -/*! - Returns the applications activation window if one has been set by - calling setActivationWindow(), otherwise returns 0. - - \sa setActivationWindow() -*/ -QWidget* QtSingleApplication::activationWindow() const -{ - return actWin; -} - - -/*! - De-minimizes, raises, and activates this application's activation window. - This function does nothing if no activation window has been set. - - This is a convenience function to show the user that this - application instance has been activated when he has tried to start - another instance. - - This function should typically be called in response to the - messageReceived() signal. By default, that will happen - automatically, if an activation window has been set. - - \sa setActivationWindow(), messageReceived(), initialize() -*/ -void QtSingleApplication::activateWindow() -{ - if (actWin) { - actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); - actWin->raise(); - actWin->activateWindow(); - } -} - - -/*! - \fn void QtSingleApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage(), setActivationWindow(), activateWindow() -*/ - - -/*! - \fn void QtSingleApplication::initialize(bool dummy = true) - - \obsolete -*/ diff --git a/src/libtomahawk/qtsingleapp/qtsingleapplication.h b/src/libtomahawk/qtsingleapp/qtsingleapplication.h deleted file mode 100644 index c696d60ce..000000000 --- a/src/libtomahawk/qtsingleapp/qtsingleapplication.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include - -#include "dllmacro.h" - -class QtLocalPeer; - -class DLLEXPORT QtSingleApplication : public QApplication -{ - Q_OBJECT - -public: - QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); - QtSingleApplication(const QString &id, int &argc, char **argv); - QtSingleApplication(int &argc, char **argv, Type type); -#if defined(Q_WS_X11) - QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); - QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); - QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); -#endif - - bool isRunning(); - QString id() const; - - void setActivationWindow(QWidget* aw, bool activateOnMessage = true); - QWidget* activationWindow() const; - - // Obsolete: - void initialize(bool dummy = true) - { isRunning(); Q_UNUSED(dummy) } - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - void activateWindow(); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - void sysInit(const QString &appId = QString()); - QtLocalPeer *peer; - QWidget *actWin; -}; diff --git a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp b/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp deleted file mode 100644 index cf607710e..000000000 --- a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtsinglecoreapplication.h" -#include "qtlocalpeer.h" - -/*! - \class QtSingleCoreApplication qtsinglecoreapplication.h - \brief A variant of the QtSingleApplication class for non-GUI applications. - - This class is a variant of QtSingleApplication suited for use in - console (non-GUI) applications. It is an extension of - QCoreApplication (instead of QApplication). It does not require - the QtGui library. - - The API and usage is identical to QtSingleApplication, except that - functions relating to the "activation window" are not present, for - obvious reasons. Please refer to the QtSingleApplication - documentation for explanation of the usage. - - A QtSingleCoreApplication instance can communicate to a - QtSingleApplication instance if they share the same application - id. Hence, this class can be used to create a light-weight - command-line tool that sends commands to a GUI application. - - \sa QtSingleApplication -*/ - -/*! - Creates a QtSingleCoreApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc and \a - argv are passed on to the QCoreAppliation constructor. -*/ - -QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleCoreApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QCoreAppliation constructor. -*/ -QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleCoreApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleCoreApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ - -bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ - -QString QtSingleCoreApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - \fn void QtSingleCoreApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage() -*/ diff --git a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h b/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h deleted file mode 100644 index ef529a8f6..000000000 --- a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include - -class QtLocalPeer; - -class QtSingleCoreApplication : public QCoreApplication -{ - Q_OBJECT - -public: - QtSingleCoreApplication(int &argc, char **argv); - QtSingleCoreApplication(const QString &id, int &argc, char **argv); - - bool isRunning(); - QString id() const; - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - QtLocalPeer* peer; -}; diff --git a/src/main.cpp b/src/main.cpp index b5c84dae6..a3277a55f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,8 @@ #endif #include + +#include "kdsingleapplicationguard/kdsingleapplicationguard.h" int main( int argc, char *argv[] ) { @@ -40,21 +42,17 @@ main( int argc, char *argv[] ) #endif - try - { - TomahawkApp a( argc, argv ); + TomahawkApp a( argc, argv ); + KDSingleApplicationGuard guard( &a, KDSingleApplicationGuard::AutoKillOtherInstances ); + QObject::connect( &guard, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ), &a, SLOT( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + + QString locale = QLocale::system().name(); - QString locale = QLocale::system().name(); - - QTranslator translator; - translator.load( QString( ":/lang/tomahawk_" ) + locale ); - a.installTranslator( &translator ); - return a.exec(); - } - catch( const std::runtime_error& e ) - { - return 0; - } + QTranslator translator; + translator.load( QString( ":/lang/tomahawk_" ) + locale ); + a.installTranslator( &translator ); + return a.exec(); + } #ifdef Q_WS_MAC diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index a148d9a02..1efa811d3 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -152,14 +152,6 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) { qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); - // send the first arg to an already running instance, but don't open twice no matter what - if( ( argc > 1 && sendMessage( argv[ 1 ] ) ) || sendMessage( "" ) ) { - qDebug() << "Sent message, already exists"; - throw runtime_error( "Already Running" ); - } - - connect( this, SIGNAL( messageReceived( QString ) ), this, SLOT( messageReceived( QString ) ) ); - #ifdef TOMAHAWK_HEADLESS m_headless = true; #else @@ -535,13 +527,15 @@ TomahawkApp::loadUrl( const QString& url ) void -TomahawkApp::messageReceived( const QString& msg ) +TomahawkApp::instanceStarted( KDSingleApplicationGuard::Instance instance ) { - qDebug() << "MESSAGE RECEIVED" << msg; - if( msg.isEmpty() ) { + qDebug() << "INSTANCE STARTED!" << instance.pid << instance.arguments; + + if( instance.arguments.size() < 2 ) + { return; } - loadUrl( msg ); + loadUrl( instance.arguments.at( 1 ) ); } From a848561e60e7af4a175ea5b47b5a9b78963cf74b Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 2 Apr 2011 19:29:53 -0400 Subject: [PATCH 49/63] Add some debugging for Chris and fix duplicate paths being queued --- src/musicscanner.cpp | 2 ++ src/scanmanager.cpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index 6ccbf14ae..d9404a18a 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -67,6 +67,8 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) foreach( const QFileInfo& di, dirs ) { + qDebug() << "Considering dir " << di.absoluteFilePath(); + qDebug() << "m_dirtimes contains it? " << (m_dirmtimes.contains( di.absoluteFilePath() ) ? "true" : "false"); if( mode == DirLister::Recursive || !m_dirmtimes.contains( di.absoluteFilePath() ) ) scanDir( di.absoluteFilePath(), depth + 1, DirLister::Recursive ); else //should be the non-recursive case since the second test above should only happen with a new dir diff --git a/src/scanmanager.cpp b/src/scanmanager.cpp index 945d42987..0073c73b9 100644 --- a/src/scanmanager.cpp +++ b/src/scanmanager.cpp @@ -219,7 +219,8 @@ ScanManager::handleChangedDir( const QString& path ) { qDebug() << Q_FUNC_INFO; qDebug() << "Dir changed: " << path; - m_queuedChangedDirs << path; + if( !m_queuedChangedDirs.contains( path ) ) + m_queuedChangedDirs << path; if( TomahawkSettings::instance()->watchForChanges() ) m_queuedScanTimer->start( 10000 ); } From 743575b91a1640412e7584b76a64f6c2f069f1d1 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 2 Apr 2011 19:34:24 -0400 Subject: [PATCH 50/63] use our export macro that works on windows :) --- .../kdsingleapplicationguard/kdsingleapplicationguard.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h index f649ef836..14706e4d0 100644 --- a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h +++ b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h @@ -5,6 +5,7 @@ #include #include "pimpl_ptr.h" +#include "dllmacro.h" class QCoreApplication; @@ -12,7 +13,7 @@ class QCoreApplication; void SIGINT_handler( int sig ); #endif -class KDTOOLSCORE_EXPORT KDSingleApplicationGuard : public QObject +class DLLEXPORT KDSingleApplicationGuard : public QObject { Q_OBJECT #ifndef Q_WS_WIN From c3df88d0f82d677886dc060372c2c4b885c3af52 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 2 Apr 2011 20:48:39 -0400 Subject: [PATCH 51/63] More debug, and fixed watch behavior :-) --- src/musicscanner.cpp | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index d9404a18a..dc0647c73 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -19,6 +19,7 @@ #include "musicscanner.h" #include "tomahawk/tomahawkapp.h" +#include "tomahawksettings.h" #include "sourcelist.h" #include "database/database.h" #include "database/databasecommand_dirmtimes.h" @@ -31,6 +32,31 @@ using namespace Tomahawk; void DirLister::go() { + qDebug() << Q_FUNC_INFO; + qDebug() << "Current mtimes: " << m_dirmtimes; + qDebug() << "Recursive? : " << (m_recursive ? "true" : "false"); + if( !m_recursive ) + { + foreach( QString dir, m_dirs ) + { + if( m_dirmtimes.contains( dir ) ) + { + qDebug() << "Removing " << dir << " from m_dirmtimes because it's specifically requested"; + m_dirmtimes.remove( dir ); + } + QStringList filtered = QStringList( m_dirmtimes.keys() ).filter( dir ); + foreach( QString filteredDir, filtered ) + { + if( !QDir( filteredDir ).exists() ) + { + qDebug() << "Removing " << filteredDir << " from m_dirmtimes because it does not exist"; + m_dirmtimes.remove( filteredDir ); + } + } + } + m_newdirmtimes = m_dirmtimes; + } + foreach( QString dir, m_dirs ) scanDir( QDir( dir, 0 ), 0, ( m_recursive ? DirLister::Recursive : DirLister::NonRecursive ) ); emit finished( m_newdirmtimes ); @@ -68,11 +94,11 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) foreach( const QFileInfo& di, dirs ) { qDebug() << "Considering dir " << di.absoluteFilePath(); - qDebug() << "m_dirtimes contains it? " << (m_dirmtimes.contains( di.absoluteFilePath() ) ? "true" : "false"); + qDebug() << "m_dirmtimes contains it? " << (m_dirmtimes.contains( di.absoluteFilePath() ) ? "true" : "false"); if( mode == DirLister::Recursive || !m_dirmtimes.contains( di.absoluteFilePath() ) ) scanDir( di.absoluteFilePath(), depth + 1, DirLister::Recursive ); - else //should be the non-recursive case since the second test above should only happen with a new dir - scanDir( di.absoluteFilePath(), depth + 1, DirLister::MTimeOnly ); + //else //should be the non-recursive case since the second test above should only happen with a new dir + // scanDir( di.absoluteFilePath(), depth + 1, DirLister::MTimeOnly ); } } @@ -130,8 +156,7 @@ MusicScanner::startScan() m_skippedFiles.clear(); // trigger the scan once we've loaded old mtimes for dirs below our path - //FIXME: MULTIPLECOLLECTIONDIRS - DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( m_dirs.first() ); + DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( TomahawkSettings::instance()->scannerPaths() ); connect( cmd, SIGNAL( done( QMap ) ), SLOT( setMtimes( QMap ) ) ); connect( cmd, SIGNAL( done( QMap ) ), From daa822cd147594645832526f36d21526c41b45f9 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 2 Apr 2011 21:44:48 -0400 Subject: [PATCH 52/63] Some minor work on infosystem --- include/tomahawk/infosystem.h | 13 +++++---- src/infosystem/infosystem.cpp | 45 ++++++++++++++++++++++---------- src/infosystem/infosystemcache.h | 7 +++++ 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/include/tomahawk/infosystem.h b/include/tomahawk/infosystem.h index 30c43f5d2..75e365362 100644 --- a/include/tomahawk/infosystem.h +++ b/include/tomahawk/infosystem.h @@ -107,15 +107,18 @@ public: qDebug() << Q_FUNC_INFO; } - virtual void getInfo( const QString &caller, const InfoType type, const QVariant &data, Tomahawk::InfoSystem::InfoCustomDataHash customData ) = 0; + virtual void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ) = 0; signals: void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); void getCachedInfo( QHash< QString, QString > criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); void finished( QString, Tomahawk::InfoSystem::InfoType ); -//public slots: - //void notInCacheSlot( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ) = 0; +public slots: + //FIXME: Make pure virtual when everything supports it + void notInCacheSlot( QHash criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ) + { + } protected: InfoType m_type; @@ -145,7 +148,7 @@ signals: public slots: void infoSlot( QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); - void finishedSlot( QString target,Tomahawk::InfoSystem::InfoType type); + void finishedSlot( QString target, Tomahawk::InfoSystem::InfoType type); private: QLinkedList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; @@ -155,7 +158,7 @@ private: // For now, statically instantiate plugins; this is just somewhere to keep them QLinkedList< InfoPluginPtr > m_plugins; - QHash< QString, QHash< Tomahawk::InfoSystem::InfoType, int > > m_dataTracker; + QHash< QString, QHash< InfoType, int > > m_dataTracker; InfoSystemCache* m_cache; QThread* m_infoSystemCacheThreadController; diff --git a/src/infosystem/infosystem.cpp b/src/infosystem/infosystem.cpp index e4a6a6c2a..deba612f0 100644 --- a/src/infosystem/infosystem.cpp +++ b/src/infosystem/infosystem.cpp @@ -25,7 +25,11 @@ #include "infoplugins/musixmatchplugin.h" #include "infoplugins/lastfmplugin.h" -using namespace Tomahawk::InfoSystem; +namespace Tomahawk +{ + +namespace InfoSystem +{ InfoPlugin::InfoPlugin(QObject *parent) :QObject( parent ) @@ -33,10 +37,11 @@ InfoPlugin::InfoPlugin(QObject *parent) qDebug() << Q_FUNC_INFO; InfoSystem *system = qobject_cast< InfoSystem* >( parent ); if( system ) - QObject::connect( system->getCache(), - SIGNAL( notInCache( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ), + QObject::connect( + system->getCache(), + SIGNAL( notInCache( QHash< QString, QString >, QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ), this, - SLOT( notInCacheSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ) + SLOT( notInCacheSlot( QHash< QString, QString >, QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ) ); } @@ -49,7 +54,7 @@ InfoSystem::InfoSystem(QObject *parent) qRegisterMetaType >("Tomahawk::InfoSystem::InfoCustomDataHash"); m_infoSystemCacheThreadController = new QThread( this ); - m_cache = new Tomahawk::InfoSystem::InfoSystemCache(); + m_cache = new InfoSystemCache(); m_cache->moveToThread( m_infoSystemCacheThreadController ); m_infoSystemCacheThreadController->start( QThread::IdlePriority ); @@ -59,6 +64,18 @@ InfoSystem::InfoSystem(QObject *parent) m_plugins.append(mmptr); InfoPluginPtr lfmptr(new LastFmPlugin(this)); m_plugins.append(lfmptr); + + Q_FOREACH( InfoPluginPtr plugin, m_plugins ) + { + connect(plugin.data(), SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), + this, SLOT(infoSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), Qt::UniqueConnection); + connect(plugin.data(), SIGNAL(finished(QString, Tomahawk::InfoSystem::InfoType)), + this, SLOT(finishedSlot(QString, Tomahawk::InfoSystem::InfoType)), Qt::UniqueConnection); + } + connect(m_cache, SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), + this, SLOT(infoSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), Qt::UniqueConnection); + connect(m_cache, SIGNAL(finished(QString, Tomahawk::InfoSystem::InfoType)), + this, SLOT(finishedSlot(QString, Tomahawk::InfoSystem::InfoType)), Qt::UniqueConnection); } InfoSystem::~InfoSystem() @@ -114,7 +131,7 @@ void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVari QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type); if (providers.isEmpty()) { - emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + emit info(QString(), InfoNoInfo, QVariant(), QVariant(), customData); emit finished(caller); return; } @@ -122,17 +139,13 @@ void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVari InfoPluginPtr ptr = providers.first(); if (!ptr) { - emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + emit info(QString(), InfoNoInfo, QVariant(), QVariant(), customData); emit finished(caller); return; } m_dataTracker[caller][type] = m_dataTracker[caller][type] + 1; qDebug() << "current count in dataTracker for type" << type << "is" << m_dataTracker[caller][type]; - connect(ptr.data(), SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), - this, SLOT(infoSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), Qt::UniqueConnection); - connect(ptr.data(), SIGNAL(finished(QString, Tomahawk::InfoSystem::InfoType)), - this, SLOT(finishedSlot(QString, Tomahawk::InfoSystem::InfoType)), Qt::UniqueConnection); ptr.data()->getInfo(caller, type, data, customData); } @@ -142,7 +155,7 @@ void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustom getInfo(caller, type, input[type], customData); } -void InfoSystem::infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData) +void InfoSystem::infoSlot(QString target, InfoType type, QVariant input, QVariant output, InfoCustomDataHash customData) { qDebug() << Q_FUNC_INFO; qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; @@ -154,12 +167,12 @@ void InfoSystem::infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, Q emit info(target, type, input, output, customData); } -void InfoSystem::finishedSlot(QString target, Tomahawk::InfoSystem::InfoType type) +void InfoSystem::finishedSlot(QString target, InfoType type) { qDebug() << Q_FUNC_INFO; m_dataTracker[target][type] = m_dataTracker[target][type] - 1; qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; - Q_FOREACH(Tomahawk::InfoSystem::InfoType testtype, m_dataTracker[target].keys()) + Q_FOREACH(InfoType testtype, m_dataTracker[target].keys()) { if (m_dataTracker[target][testtype] != 0) { @@ -170,3 +183,7 @@ void InfoSystem::finishedSlot(QString target, Tomahawk::InfoSystem::InfoType typ qDebug() << "emitting finished with target" << target; emit finished(target); } + +} //namespace InfoSystem + +} //namespace Tomahawk \ No newline at end of file diff --git a/src/infosystem/infosystemcache.h b/src/infosystem/infosystemcache.h index 97990b7a1..89aa9df6e 100644 --- a/src/infosystem/infosystemcache.h +++ b/src/infosystem/infosystemcache.h @@ -22,6 +22,8 @@ #include #include +#include "tomahawk/infosystem.h" + namespace Tomahawk { @@ -44,6 +46,11 @@ public: qDebug() << Q_FUNC_INFO; } +signals: + void notInCache( QHash< QString, QString > criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void finished( QString, Tomahawk::InfoSystem::InfoType ); + }; } //namespace InfoSystem From 1da0a34d8921dfad2a518532b0a8bd3004db7927 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 22 Mar 2011 21:33:27 -0400 Subject: [PATCH 53/63] tomahawk:// handler stuff on windows --- admin/win/nsi/tomahawk.nsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi index c2d2a9bd1..222defc2b 100644 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -457,6 +457,12 @@ Section -post WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "NoModify" "1" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "NoRepair" "1" + ; Register tomahawk:// protocol handler + WriteRegStr HKCR "tomahawk" "" "URL: Tomahawk Protocol" + WriteRegStr HKCR "tomahawk\DefaultIcon" "" $INSTDIR\tomahawk.exe,1 + WriteRegStr HKCR "tomahawk\shell" "" "open" + WriteRegStr HKCR "tomahawk\shell\open\command" "" '"$INSTDIR\tomahawk.exe" "%1"' + SetDetailsPrint textonly DetailPrint "Finsihed." SectionEnd @@ -516,6 +522,8 @@ Section Uninstall DeleteRegValue HKLM "Software\Tomahawk" "" DeleteRegKey HKLM "Software\Tomahawk" + DeleteRegKey HKCR "tomahawk" + ;Start menu shortcuts. !ifdef OPTION_SECTION_SC_START_MENU SetShellVarContext all From 2b85beb704c7917f7e97466e504f1168331ad998 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 2 Apr 2011 22:49:11 -0400 Subject: [PATCH 54/63] Requests for cover art from lastfm now go through the infosystem caching mechanism. It's not actually storing a cache yet but the information flow works (at least, for cache misses :-) ) --- include/tomahawk/infosystem.h | 23 +++--- src/audiocontrols.cpp | 14 ++-- src/audiocontrols.h | 2 +- src/infosystem/infoplugins/echonestplugin.cpp | 18 ++--- src/infosystem/infoplugins/echonestplugin.h | 20 ++--- src/infosystem/infoplugins/lastfmplugin.cpp | 70 +++++++++++------ src/infosystem/infoplugins/lastfmplugin.h | 11 +-- .../infoplugins/musixmatchplugin.cpp | 24 +++--- src/infosystem/infoplugins/musixmatchplugin.h | 4 +- src/infosystem/infosystem.cpp | 76 +++++++++++++------ src/infosystem/infosystemcache.cpp | 34 +++++++++ src/infosystem/infosystemcache.h | 8 +- src/scrobbler.cpp | 12 +-- src/scrobbler.h | 2 +- src/xmppbot/xmppbot.cpp | 16 ++-- src/xmppbot/xmppbot.h | 2 +- 16 files changed, 215 insertions(+), 121 deletions(-) diff --git a/include/tomahawk/infosystem.h b/include/tomahawk/infosystem.h index 75e365362..2ab5d2da0 100644 --- a/include/tomahawk/infosystem.h +++ b/include/tomahawk/infosystem.h @@ -93,7 +93,8 @@ enum InfoType { typedef QMap< InfoType, QVariant > InfoMap; typedef QMap< QString, QMap< QString, QString > > InfoGenericMap; -typedef QHash< QString, QVariant > InfoCustomDataHash; +typedef QHash< QString, QVariant > InfoCustomData; +typedef QHash< QString, QString > InfoCacheCriteria; class InfoPlugin : public QObject { @@ -107,16 +108,17 @@ public: qDebug() << Q_FUNC_INFO; } - virtual void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ) = 0; + virtual void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ) = 0; signals: - void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); - void getCachedInfo( QHash< QString, QString > criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void getCachedInfo( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); + void updateCache( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output ); + void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); void finished( QString, Tomahawk::InfoSystem::InfoType ); public slots: //FIXME: Make pure virtual when everything supports it - void notInCacheSlot( QHash criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ) + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ) { } @@ -137,17 +139,17 @@ public: void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &types ); - void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ); - void getInfo( const QString &caller, const InfoMap &input, InfoCustomDataHash customData ); + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ); + void getInfo( const QString &caller, const InfoMap &input, InfoCustomData customData ); InfoSystemCache* getCache() { return m_cache; } signals: - void info( QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void info( QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); void finished( QString target ); public slots: - void infoSlot( QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void infoSlot( QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); void finishedSlot( QString target, Tomahawk::InfoSystem::InfoType type); private: @@ -169,6 +171,7 @@ private: } Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoGenericMap ); -Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCustomDataHash ); +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCustomData ); +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCacheCriteria ); #endif // TOMAHAWK_INFOSYSTEM_H diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp index cd6f7682e..8f4214ed8 100644 --- a/src/audiocontrols.cpp +++ b/src/audiocontrols.cpp @@ -168,8 +168,8 @@ AudioControls::AudioControls( QWidget* parent ) .scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); connect( TomahawkApp::instance()->infoSystem(), - SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ), - SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ) ); + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) ); connect( TomahawkApp::instance()->infoSystem(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); @@ -252,17 +252,17 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result ) QString artistName = result->artist()->name(); QString albumName = result->album()->name(); - Tomahawk::InfoSystem::InfoCustomDataHash trackInfo; + Tomahawk::InfoSystem::InfoCustomData trackInfo; trackInfo["artist"] = QVariant::fromValue< QString >( result->artist()->name() ); trackInfo["album"] = QVariant::fromValue< QString >( result->album()->name() ); TomahawkApp::instance()->infoSystem()->getInfo( s_infoIdentifier, Tomahawk::InfoSystem::InfoAlbumCoverArt, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomDataHash >( trackInfo ), Tomahawk::InfoSystem::InfoCustomDataHash() ); + QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomData >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); } void -AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ) +AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) { qDebug() << Q_FUNC_INFO; if ( caller != s_infoIdentifier || type != Tomahawk::InfoSystem::InfoAlbumCoverArt ) @@ -277,13 +277,13 @@ AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType ty return; } - if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomDataHash >() ) + if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) { qDebug() << "Cannot convert fetched art from a QByteArray"; return; } - Tomahawk::InfoSystem::InfoCustomDataHash returnedData = output.value< Tomahawk::InfoSystem::InfoCustomDataHash >(); + Tomahawk::InfoSystem::InfoCustomData returnedData = output.value< Tomahawk::InfoSystem::InfoCustomData >(); const QByteArray ba = returnedData["imgbytes"].toByteArray(); if ( ba.length() ) { diff --git a/src/audiocontrols.h b/src/audiocontrols.h index 0491745c3..3b90005d6 100644 --- a/src/audiocontrols.h +++ b/src/audiocontrols.h @@ -45,7 +45,7 @@ signals: public slots: void onRepeatModeChanged( PlaylistInterface::RepeatMode mode ); void onShuffleModeChanged( bool enabled ); - void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); void infoSystemFinished( QString target ); protected: diff --git a/src/infosystem/infoplugins/echonestplugin.cpp b/src/infosystem/infoplugins/echonestplugin.cpp index fab7c9bc4..ec852f787 100644 --- a/src/infosystem/infoplugins/echonestplugin.cpp +++ b/src/infosystem/infoplugins/echonestplugin.cpp @@ -41,7 +41,7 @@ EchoNestPlugin::~EchoNestPlugin() qDebug() << Q_FUNC_INFO; } -void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData) +void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomData customData) { switch (type) { @@ -65,7 +65,7 @@ void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const Q } } -void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, InfoCustomDataHash &customData, const QString &item) +void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, InfoCustomData &customData, const QString &item) { //WARNING: Totally not implemented yet @@ -80,7 +80,7 @@ void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, // connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); } -void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; @@ -94,7 +94,7 @@ void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& d connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); } -void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; @@ -109,7 +109,7 @@ void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& connect(reply, SIGNAL(finished()), SLOT(getArtistFamiliaritySlot())); } -void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; @@ -123,7 +123,7 @@ void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& connect(reply, SIGNAL(finished()), SLOT(getArtistHotttnesssSlot())); } -void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; @@ -137,7 +137,7 @@ void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, connect(reply, SIGNAL(finished()), SLOT(getArtistTermsSlot())); } -void EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant& data, InfoCustomDataHash& customData) +void EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant& data, InfoCustomData& customData) { QNetworkReply* reply = Echonest::Artist::topTerms( 20 ); m_replyMap[reply] = customData; @@ -230,7 +230,7 @@ void EchoNestPlugin::getMiscTopSlot() reply->deleteLater(); } -bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& data, InfoCustomData &customData) { if (data.isNull() || !data.isValid() || !data.canConvert()) { @@ -246,7 +246,7 @@ bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& da return true; } -bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& data, InfoCustomData &customData) { if (data.isNull() || !data.isValid() || !data.canConvert()) { diff --git a/src/infosystem/infoplugins/echonestplugin.h b/src/infosystem/infoplugins/echonestplugin.h index 734ceecd2..d4a4db367 100644 --- a/src/infosystem/infoplugins/echonestplugin.h +++ b/src/infosystem/infoplugins/echonestplugin.h @@ -42,18 +42,18 @@ public: EchoNestPlugin(QObject *parent); virtual ~EchoNestPlugin(); - void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ); + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ); private: - void getSongProfile( const QString &caller, const QVariant &data, InfoCustomDataHash &customData, const QString &item = QString() ); - void getArtistBiography ( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getArtistFamiliarity( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getArtistHotttnesss( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getArtistTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getMiscTopTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + void getSongProfile( const QString &caller, const QVariant &data, InfoCustomData &customData, const QString &item = QString() ); + void getArtistBiography ( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getArtistFamiliarity( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getArtistHotttnesss( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getArtistTerms( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getMiscTopTerms( const QString &caller, const QVariant &data, InfoCustomData &customData ); - bool isValidArtistData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData ); - bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData ); + bool isValidArtistData( const QString &caller, const QVariant& data, InfoCustomData& customData ); + bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomData& customData ); Echonest::Artist artistFromReply( QNetworkReply* ); private slots: @@ -64,7 +64,7 @@ private slots: void getMiscTopSlot(); private: - QHash< QNetworkReply*, InfoCustomDataHash > m_replyMap; + QHash< QNetworkReply*, InfoCustomData > m_replyMap; QHash< QNetworkReply*, QString > m_callerMap; }; diff --git a/src/infosystem/infoplugins/lastfmplugin.cpp b/src/infosystem/infoplugins/lastfmplugin.cpp index 81272b86f..5848f6f68 100644 --- a/src/infosystem/infoplugins/lastfmplugin.cpp +++ b/src/infosystem/infoplugins/lastfmplugin.cpp @@ -92,7 +92,7 @@ LastFmPlugin::~LastFmPlugin() } void -LastFmPlugin::dataError( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData ) +LastFmPlugin::dataError( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) { emit info( caller, type, data, QVariant(), customData ); emit finished( caller, type ); @@ -100,7 +100,7 @@ LastFmPlugin::dataError( const QString &caller, const InfoType type, const QVari } void -LastFmPlugin::getInfo( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash customData ) +LastFmPlugin::getInfo( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData customData ) { qDebug() << Q_FUNC_INFO; if ( type == InfoMiscSubmitNowPlaying ) @@ -114,14 +114,14 @@ LastFmPlugin::getInfo( const QString &caller, const InfoType type, const QVarian } void -LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData ) +LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) { - if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomDataHash >() || !m_scrobbler ) + if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomData >() || !m_scrobbler ) { dataError( caller, type, data, customData ); return; } - InfoCustomDataHash hash = data.value< Tomahawk::InfoSystem::InfoCustomDataHash >(); + InfoCustomData hash = data.value< Tomahawk::InfoSystem::InfoCustomData >(); if ( !hash.contains( "title" ) || !hash.contains( "artist" ) || !hash.contains( "album" ) || !hash.contains( "duration" ) ) { dataError( caller, type, data, customData ); @@ -143,7 +143,7 @@ LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVar } void -LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData ) +LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) { Q_ASSERT( QThread::currentThread() == thread() ); @@ -162,33 +162,50 @@ LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVaria } void -LastFmPlugin::fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData ) +LastFmPlugin::fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) { qDebug() << Q_FUNC_INFO; - if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomDataHash >() ) + if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) { dataError( caller, type, data, customData ); return; } - InfoCustomDataHash hash = data.value< Tomahawk::InfoSystem::InfoCustomDataHash >(); + InfoCustomData hash = data.value< Tomahawk::InfoSystem::InfoCustomData >(); if ( !hash.contains( "artist" ) || !hash.contains( "album" ) ) { dataError( caller, type, data, customData ); return; } - QString artistName = hash["artist"].toString(); - QString albumName = hash["album"].toString(); + Tomahawk::InfoSystem::InfoCacheCriteria criteria; + criteria["artist"] = hash["artist"].toString(); + criteria["album"] = hash["album"].toString(); - QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b"; - QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); - QNetworkReply* reply = TomahawkUtils::nam()->get( req ); - reply->setProperty("customData", QVariant::fromValue(customData)); - reply->setProperty("origData", data); - reply->setProperty("caller", caller); - reply->setProperty("type", (uint)(type) ); + emit getCachedInfo( criteria, caller, type, data, customData ); +} - connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); +void +LastFmPlugin::notInCacheSlot( QHash criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ) +{ + qDebug() << Q_FUNC_INFO; + if ( type == InfoAlbumCoverArt ) + { + QString artistName = criteria["artist"]; + QString albumName = criteria["album"]; + + QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b"; + QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); + QNetworkReply* reply = TomahawkUtils::nam()->get( req ); + reply->setProperty( "customData", QVariant::fromValue( customData ) ); + reply->setProperty( "origData", input ); + reply->setProperty( "caller", caller ); + reply->setProperty( "type", (uint)(type) ); + + connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); + return; + } + else + qDebug() << "Couldn't figure out what to do with this type of request after cache miss"; } void @@ -200,17 +217,26 @@ LastFmPlugin::coverArtReturned() if ( redir.isEmpty() ) { const QByteArray ba = reply->readAll(); - Tomahawk::InfoSystem::InfoCustomDataHash returnedData; + InfoCustomData returnedData; returnedData["imgbytes"] = ba; returnedData["url"] = reply->url().toString(); + + InfoCustomData customData = reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); + InfoType type = (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()); emit info( reply->property( "caller" ).toString(), - (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()), + type, reply->property( "origData" ), returnedData, - reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomDataHash >() + customData ); emit finished( reply->property( "caller" ).toString(), (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()) ); + + InfoCustomData origData = reply->property( "origData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); + Tomahawk::InfoSystem::InfoCacheCriteria criteria; + criteria["artist"] = origData["artist"].toString(); + criteria["album"] = origData["album"].toString(); + emit updateCache( criteria, type, returnedData ); } else { diff --git a/src/infosystem/infoplugins/lastfmplugin.h b/src/infosystem/infoplugins/lastfmplugin.h index 3f396edfb..589d40db1 100644 --- a/src/infosystem/infoplugins/lastfmplugin.h +++ b/src/infosystem/infoplugins/lastfmplugin.h @@ -43,19 +43,20 @@ public: LastFmPlugin( QObject *parent ); virtual ~LastFmPlugin(); - void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ); + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ); public slots: void settingsChanged(); void onAuthenticated(); void coverArtReturned(); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); private: - void fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData ); - void scrobble( const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash &customData ); + void fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ); + void scrobble( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData ); void createScrobbler(); - void nowPlaying( const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash &customData ); - void dataError( const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash &customData ); + void nowPlaying( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData ); + void dataError( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData ); lastfm::MutableTrack m_track; lastfm::Audioscrobbler* m_scrobbler; diff --git a/src/infosystem/infoplugins/musixmatchplugin.cpp b/src/infosystem/infoplugins/musixmatchplugin.cpp index 0ee319f78..cc2cd576f 100644 --- a/src/infosystem/infoplugins/musixmatchplugin.cpp +++ b/src/infosystem/infoplugins/musixmatchplugin.cpp @@ -42,12 +42,12 @@ MusixMatchPlugin::~MusixMatchPlugin() qDebug() << Q_FUNC_INFO; } -void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash customData) +void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData customData) { qDebug() << Q_FUNC_INFO; - if( !isValidTrackData(caller, data, customData) || !data.canConvert()) + if( !isValidTrackData(caller, data, customData) || !data.canConvert()) return; - Tomahawk::InfoSystem::InfoCustomDataHash hash = data.value(); + Tomahawk::InfoSystem::InfoCustomData hash = data.value(); QString artist = hash["artistName"].toString(); QString track = hash["trackName"].toString(); if( artist.isEmpty() || track.isEmpty() ) @@ -63,24 +63,24 @@ void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const url.addQueryItem("q_artist", artist); url.addQueryItem("q_track", track); QNetworkReply* reply = TomahawkUtils::nam()->get(QNetworkRequest(url)); - reply->setProperty("customData", QVariant::fromValue(customData)); + reply->setProperty("customData", QVariant::fromValue(customData)); reply->setProperty("origData", data); reply->setProperty("caller", caller); connect(reply, SIGNAL(finished()), SLOT(trackSearchSlot())); } -bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData) +bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData) { qDebug() << Q_FUNC_INFO; - if (data.isNull() || !data.isValid() || !data.canConvert()) + if (data.isNull() || !data.isValid() || !data.canConvert()) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert"; return false; } - InfoCustomDataHash hash = data.value(); + InfoCustomData hash = data.value(); if (hash["trackName"].toString().isEmpty() ) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); @@ -104,7 +104,7 @@ void MusixMatchPlugin::trackSearchSlot() QNetworkReply* oldReply = qobject_cast( sender() ); if (!oldReply) { - emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash()); + emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomData()); return; } QDomDocument doc; @@ -113,7 +113,7 @@ void MusixMatchPlugin::trackSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName("track_id"); if (domNodeList.isEmpty()) { - emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value()); + emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value()); emit finished(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); return; } @@ -135,7 +135,7 @@ void MusixMatchPlugin::trackLyricsSlot() QNetworkReply* reply = qobject_cast( sender() ); if (!reply) { - emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash()); + emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomData()); return; } QDomDocument doc; @@ -143,12 +143,12 @@ void MusixMatchPlugin::trackLyricsSlot() QDomNodeList domNodeList = doc.elementsByTagName("lyrics_body"); if (domNodeList.isEmpty()) { - emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value()); + emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value()); emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); return; } QString lyrics = domNodeList.at(0).toElement().text(); qDebug() << "Emitting lyrics: " << lyrics; - emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value()); + emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value()); emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); } diff --git a/src/infosystem/infoplugins/musixmatchplugin.h b/src/infosystem/infoplugins/musixmatchplugin.h index 284c81515..255ebd53d 100644 --- a/src/infosystem/infoplugins/musixmatchplugin.h +++ b/src/infosystem/infoplugins/musixmatchplugin.h @@ -36,10 +36,10 @@ public: MusixMatchPlugin(QObject *parent); virtual ~MusixMatchPlugin(); - void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData); + void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData); private: - bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash &customData ); + bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomData &customData ); public slots: void trackSearchSlot(); diff --git a/src/infosystem/infosystem.cpp b/src/infosystem/infosystem.cpp index deba612f0..d03b53f10 100644 --- a/src/infosystem/infosystem.cpp +++ b/src/infosystem/infosystem.cpp @@ -37,12 +37,26 @@ InfoPlugin::InfoPlugin(QObject *parent) qDebug() << Q_FUNC_INFO; InfoSystem *system = qobject_cast< InfoSystem* >( parent ); if( system ) - QObject::connect( - system->getCache(), - SIGNAL( notInCache( QHash< QString, QString >, QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ), - this, - SLOT( notInCacheSlot( QHash< QString, QString >, QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ) - ); + { + QObject::connect( + this, + SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + system->getCache(), + SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) + ); + QObject::connect( + system->getCache(), + SIGNAL( notInCache( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + this, + SLOT( notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) + ); + QObject::connect( + this, + SIGNAL( updateCache( Tomahawk::InfoSystem::InfoCacheCriteria, Tomahawk::InfoSystem::InfoType, QVariant ) ), + system->getCache(), + SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria, Tomahawk::InfoSystem::InfoType, QVariant ) ) + ); + } } @@ -50,32 +64,44 @@ InfoSystem::InfoSystem(QObject *parent) : QObject(parent) { qDebug() << Q_FUNC_INFO; - qRegisterMetaType > >("Tomahawk::InfoSystem::InfoGenericMap"); - qRegisterMetaType >("Tomahawk::InfoSystem::InfoCustomDataHash"); + qRegisterMetaType< QMap< QString, QMap< QString, QString > > >( "Tomahawk::InfoSystem::InfoGenericMap" ); + qRegisterMetaType< QHash< QString, QVariant > >( "Tomahawk::InfoSystem::InfoCustomData" ); + qRegisterMetaType< QHash< QString, QString > >( "Tomahawk::InfoSystem::InfoCacheCriteria" ); + qRegisterMetaType< Tomahawk::InfoSystem::InfoType >( "Tomahawk::InfoSystem::InfoType" ); m_infoSystemCacheThreadController = new QThread( this ); m_cache = new InfoSystemCache(); m_cache->moveToThread( m_infoSystemCacheThreadController ); m_infoSystemCacheThreadController->start( QThread::IdlePriority ); - InfoPluginPtr enptr(new EchoNestPlugin(this)); - m_plugins.append(enptr); - InfoPluginPtr mmptr(new MusixMatchPlugin(this)); - m_plugins.append(mmptr); - InfoPluginPtr lfmptr(new LastFmPlugin(this)); - m_plugins.append(lfmptr); + InfoPluginPtr enptr( new EchoNestPlugin( this ) ); + m_plugins.append( enptr ); + InfoPluginPtr mmptr( new MusixMatchPlugin( this ) ); + m_plugins.append( mmptr ); + InfoPluginPtr lfmptr( new LastFmPlugin( this ) ); + m_plugins.append( lfmptr ); Q_FOREACH( InfoPluginPtr plugin, m_plugins ) { - connect(plugin.data(), SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), - this, SLOT(infoSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), Qt::UniqueConnection); - connect(plugin.data(), SIGNAL(finished(QString, Tomahawk::InfoSystem::InfoType)), - this, SLOT(finishedSlot(QString, Tomahawk::InfoSystem::InfoType)), Qt::UniqueConnection); + connect( + plugin.data(), + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + this, + SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + Qt::UniqueConnection + ); + + connect( + plugin.data(), + SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), + this, + SLOT( finishedSlot( QString, Tomahawk::InfoSystem::InfoType ) ), Qt::UniqueConnection + ); } - connect(m_cache, SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), - this, SLOT(infoSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), Qt::UniqueConnection); - connect(m_cache, SIGNAL(finished(QString, Tomahawk::InfoSystem::InfoType)), - this, SLOT(finishedSlot(QString, Tomahawk::InfoSystem::InfoType)), Qt::UniqueConnection); + connect( m_cache, SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + this, SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), Qt::UniqueConnection ); + connect( m_cache, SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), + this, SLOT( finishedSlot( QString, Tomahawk::InfoSystem::InfoType ) ), Qt::UniqueConnection ); } InfoSystem::~InfoSystem() @@ -125,7 +151,7 @@ QLinkedList< InfoPluginPtr > InfoSystem::determineOrderedMatches(const InfoType return providers; } -void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData) +void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomData customData) { qDebug() << Q_FUNC_INFO; QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type); @@ -149,13 +175,13 @@ void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVari ptr.data()->getInfo(caller, type, data, customData); } -void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomDataHash customData) +void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomData customData) { Q_FOREACH( InfoType type, input.keys() ) getInfo(caller, type, input[type], customData); } -void InfoSystem::infoSlot(QString target, InfoType type, QVariant input, QVariant output, InfoCustomDataHash customData) +void InfoSystem::infoSlot(QString target, InfoType type, QVariant input, QVariant output, InfoCustomData customData) { qDebug() << Q_FUNC_INFO; qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; diff --git a/src/infosystem/infosystemcache.cpp b/src/infosystem/infosystemcache.cpp index e69de29bb..c25fb10bb 100644 --- a/src/infosystem/infosystemcache.cpp +++ b/src/infosystem/infosystemcache.cpp @@ -0,0 +1,34 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk 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. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include + +#include "infosystemcache.h" + +void +Tomahawk::InfoSystem::InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ) +{ + qDebug() << Q_FUNC_INFO; + emit notInCache( criteria, caller, type, input, customData ); +} + +void +Tomahawk::InfoSystem::InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output ) +{ + qDebug() << Q_FUNC_INFO; +} diff --git a/src/infosystem/infosystemcache.h b/src/infosystem/infosystemcache.h index 89aa9df6e..44476314a 100644 --- a/src/infosystem/infosystemcache.h +++ b/src/infosystem/infosystemcache.h @@ -47,10 +47,14 @@ public: } signals: - void notInCache( QHash< QString, QString > criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); - void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void notInCache( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); + void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); void finished( QString, Tomahawk::InfoSystem::InfoType ); +public slots: + void getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); + void updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output ); + }; } //namespace InfoSystem diff --git a/src/scrobbler.cpp b/src/scrobbler.cpp index 06ebe33f5..ef599db76 100644 --- a/src/scrobbler.cpp +++ b/src/scrobbler.cpp @@ -39,8 +39,8 @@ Scrobbler::Scrobbler( QObject* parent ) SLOT( engineTick( unsigned int ) ), Qt::QueuedConnection ); connect( TomahawkApp::instance()->infoSystem(), - SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ), - SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ) ); + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) ); connect( TomahawkApp::instance()->infoSystem(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); } @@ -63,7 +63,7 @@ Scrobbler::trackStarted( const Tomahawk::result_ptr& track ) scrobble(); } - Tomahawk::InfoSystem::InfoCustomDataHash trackInfo; + Tomahawk::InfoSystem::InfoCustomData trackInfo; trackInfo["title"] = QVariant::fromValue< QString >( track->track() ); trackInfo["artist"] = QVariant::fromValue< QString >( track->artist()->name() ); @@ -71,7 +71,7 @@ Scrobbler::trackStarted( const Tomahawk::result_ptr& track ) trackInfo["duration"] = QVariant::fromValue< uint >( track->duration() ); TomahawkApp::instance()->infoSystem()->getInfo( s_infoIdentifier, Tomahawk::InfoSystem::InfoMiscSubmitNowPlaying, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomDataHash >( trackInfo ), Tomahawk::InfoSystem::InfoCustomDataHash() ); + QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomData >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); m_scrobblePoint = ScrobblePoint( track->duration() / 2 ); } @@ -119,11 +119,11 @@ Scrobbler::scrobble() TomahawkApp::instance()->infoSystem()->getInfo( s_infoIdentifier, Tomahawk::InfoSystem::InfoMiscSubmitScrobble, - QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash() ); + QVariant(), Tomahawk::InfoSystem::InfoCustomData() ); } void -Scrobbler::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ) +Scrobbler::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) { if ( caller == s_infoIdentifier ) { diff --git a/src/scrobbler.h b/src/scrobbler.h index 4ed020fba..11741beec 100644 --- a/src/scrobbler.h +++ b/src/scrobbler.h @@ -45,7 +45,7 @@ public slots: void trackStopped(); void engineTick( unsigned int secondsElapsed ); - void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); void infoSystemFinished( QString target ); private: diff --git a/src/xmppbot/xmppbot.cpp b/src/xmppbot/xmppbot.cpp index d7d267f9d..464e09ede 100644 --- a/src/xmppbot/xmppbot.cpp +++ b/src/xmppbot/xmppbot.cpp @@ -67,8 +67,8 @@ XMPPBot::XMPPBot(QObject *parent) SLOT(newTrackSlot(const Tomahawk::result_ptr &))); connect(TomahawkApp::instance()->infoSystem(), - SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), - SLOT(infoReturnedSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash))); + SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData)), + SLOT(infoReturnedSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData))); connect(TomahawkApp::instance()->infoSystem(), SIGNAL(finished(QString)), SLOT(infoFinishedSlot(QString))); @@ -215,10 +215,10 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) infoMap[InfoArtistFamiliarity] = m_currTrack.data()->artist()->name(); if (token == "lyrics") { - InfoCustomDataHash myhash; + InfoCustomData myhash; myhash["trackName"] = QVariant::fromValue(m_currTrack.data()->track()); myhash["artistName"] = QVariant::fromValue(m_currTrack.data()->artist()->name()); - infoMap[InfoTrackLyrics] = QVariant::fromValue(myhash); + infoMap[InfoTrackLyrics] = QVariant::fromValue(myhash); } } @@ -235,12 +235,12 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) QString waitMsg("Please wait..."); Message retMsg(Message::Chat, JID(originatingJid.toStdString()), waitMsg.toStdString()); m_client.data()->send(retMsg); - Tomahawk::InfoSystem::InfoCustomDataHash hash; + Tomahawk::InfoSystem::InfoCustomData hash; hash["XMPPBotSendToJID"] = originatingJid; TomahawkApp::instance()->infoSystem()->getInfo(s_infoIdentifier, infoMap, hash); } -void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData) +void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData) { qDebug() << Q_FUNC_INFO; @@ -364,13 +364,13 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty { qDebug() << "Lyrics requested"; if (!output.canConvert() || - !input.canConvert() + !input.canConvert() ) { qDebug() << "Variants failed to be valid"; break; } - InfoCustomDataHash inHash = input.value(); + InfoCustomData inHash = input.value(); QString artist = inHash["artistName"].toString(); QString track = inHash["trackName"].toString(); QString lyrics = output.toString(); diff --git a/src/xmppbot/xmppbot.h b/src/xmppbot/xmppbot.h index e88704a44..ee5a2b38a 100644 --- a/src/xmppbot/xmppbot.h +++ b/src/xmppbot/xmppbot.h @@ -66,7 +66,7 @@ public: public slots: virtual void newTrackSlot(const Tomahawk::result_ptr &track); - virtual void infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); + virtual void infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData); virtual void infoFinishedSlot(QString caller); protected: From 680b204d11d7ad3e229f3eb2960f77f399661dfb Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sun, 3 Apr 2011 05:44:32 -0400 Subject: [PATCH 55/63] Make caching work. Doesn't save/load to disk yet but it's ready to be used as a memcache at least. --- include/tomahawk/infosystem.h | 20 ++++++++++++++++++- src/infosystem/infoplugins/echonestplugin.cpp | 5 ----- src/infosystem/infoplugins/lastfmplugin.cpp | 4 ---- .../infoplugins/musixmatchplugin.cpp | 7 ------- src/infosystem/infosystem.cpp | 15 +------------- src/infosystem/infosystemcache.cpp | 13 +++++++++++- src/infosystem/infosystemcache.h | 3 ++- 7 files changed, 34 insertions(+), 33 deletions(-) diff --git a/include/tomahawk/infosystem.h b/include/tomahawk/infosystem.h index 2ab5d2da0..e0efec86b 100644 --- a/include/tomahawk/infosystem.h +++ b/include/tomahawk/infosystem.h @@ -19,6 +19,7 @@ #ifndef TOMAHAWK_INFOSYSTEM_H #define TOMAHAWK_INFOSYSTEM_H +#include #include #include #include @@ -150,7 +151,6 @@ signals: public slots: void infoSlot( QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); - void finishedSlot( QString target, Tomahawk::InfoSystem::InfoType type); private: QLinkedList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; @@ -170,6 +170,24 @@ private: } +inline uint qHash( Tomahawk::InfoSystem::InfoCacheCriteria hash ) +{ + QCryptographicHash md5( QCryptographicHash::Md5 ); + foreach( QString key, hash.keys() ) + md5.addData( key.toUtf8() ); + foreach( QString value, hash.values() ) + md5.addData( value.toUtf8() ); + + QString hexData = md5.result(); + + uint returnval = 0; + + foreach( uint val, hexData.toUcs4() ) + returnval += val; + + return returnval; +} + Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoGenericMap ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCustomData ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCacheCriteria ); diff --git a/src/infosystem/infoplugins/echonestplugin.cpp b/src/infosystem/infoplugins/echonestplugin.cpp index ec852f787..38f64ed4c 100644 --- a/src/infosystem/infoplugins/echonestplugin.cpp +++ b/src/infosystem/infoplugins/echonestplugin.cpp @@ -163,7 +163,6 @@ void EchoNestPlugin::getArtistBiographySlot() } emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography, reply->property( "data" ), QVariant::fromValue(biographyMap), m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -175,7 +174,6 @@ void EchoNestPlugin::getArtistFamiliaritySlot() Echonest::Artist artist = artistFromReply( reply ); qreal familiarity = artist.familiarity(); emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity, reply->property( "data" ), familiarity, m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -187,7 +185,6 @@ void EchoNestPlugin::getArtistHotttnesssSlot() Echonest::Artist artist = artistFromReply( reply ); qreal hotttnesss = artist.hotttnesss(); emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness, reply->property( "data" ), hotttnesss, m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -206,7 +203,6 @@ void EchoNestPlugin::getArtistTermsSlot() termsMap[ term.name() ] = termMap; } emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms, reply->property( "data" ), QVariant::fromValue(termsMap), m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -224,7 +220,6 @@ void EchoNestPlugin::getMiscTopSlot() termsMap[ term.name().toLower() ] = termMap; } emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms, QVariant(), QVariant::fromValue(termsMap), m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); diff --git a/src/infosystem/infoplugins/lastfmplugin.cpp b/src/infosystem/infoplugins/lastfmplugin.cpp index 5848f6f68..652d87414 100644 --- a/src/infosystem/infoplugins/lastfmplugin.cpp +++ b/src/infosystem/infoplugins/lastfmplugin.cpp @@ -95,7 +95,6 @@ void LastFmPlugin::dataError( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) { emit info( caller, type, data, QVariant(), customData ); - emit finished( caller, type ); return; } @@ -139,7 +138,6 @@ LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVar m_scrobbler->nowPlaying( m_track ); emit info( caller, type, data, QVariant(), customData ); - emit finished( caller, type ); } void @@ -158,7 +156,6 @@ LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVaria m_scrobbler->submit(); emit info( caller, type, data, QVariant(), customData ); - emit finished( caller, type ); } void @@ -230,7 +227,6 @@ LastFmPlugin::coverArtReturned() returnedData, customData ); - emit finished( reply->property( "caller" ).toString(), (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()) ); InfoCustomData origData = reply->property( "origData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); Tomahawk::InfoSystem::InfoCacheCriteria criteria; diff --git a/src/infosystem/infoplugins/musixmatchplugin.cpp b/src/infosystem/infoplugins/musixmatchplugin.cpp index cc2cd576f..128085ca6 100644 --- a/src/infosystem/infoplugins/musixmatchplugin.cpp +++ b/src/infosystem/infoplugins/musixmatchplugin.cpp @@ -53,7 +53,6 @@ void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const if( artist.isEmpty() || track.isEmpty() ) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); return; } qDebug() << "artist is " << artist << ", track is " << track; @@ -76,7 +75,6 @@ bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& d if (data.isNull() || !data.isValid() || !data.canConvert()) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert"; return false; } @@ -84,14 +82,12 @@ bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& d if (hash["trackName"].toString().isEmpty() ) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty"; return false; } if (hash["artistName"].toString().isEmpty() ) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found"; return false; } @@ -114,7 +110,6 @@ void MusixMatchPlugin::trackSearchSlot() if (domNodeList.isEmpty()) { emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value()); - emit finished(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); return; } QString track_id = domNodeList.at(0).toElement().text(); @@ -144,11 +139,9 @@ void MusixMatchPlugin::trackLyricsSlot() if (domNodeList.isEmpty()) { emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value()); - emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); return; } QString lyrics = domNodeList.at(0).toElement().text(); qDebug() << "Emitting lyrics: " << lyrics; emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value()); - emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); } diff --git a/src/infosystem/infosystem.cpp b/src/infosystem/infosystem.cpp index d03b53f10..7fd6158c6 100644 --- a/src/infosystem/infosystem.cpp +++ b/src/infosystem/infosystem.cpp @@ -90,18 +90,9 @@ InfoSystem::InfoSystem(QObject *parent) SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), Qt::UniqueConnection ); - - connect( - plugin.data(), - SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), - this, - SLOT( finishedSlot( QString, Tomahawk::InfoSystem::InfoType ) ), Qt::UniqueConnection - ); } connect( m_cache, SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), this, SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), Qt::UniqueConnection ); - connect( m_cache, SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), - this, SLOT( finishedSlot( QString, Tomahawk::InfoSystem::InfoType ) ), Qt::UniqueConnection ); } InfoSystem::~InfoSystem() @@ -191,11 +182,7 @@ void InfoSystem::infoSlot(QString target, InfoType type, QVariant input, QVarian return; } emit info(target, type, input, output, customData); -} - -void InfoSystem::finishedSlot(QString target, InfoType type) -{ - qDebug() << Q_FUNC_INFO; + m_dataTracker[target][type] = m_dataTracker[target][type] - 1; qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; Q_FOREACH(InfoType testtype, m_dataTracker[target].keys()) diff --git a/src/infosystem/infosystemcache.cpp b/src/infosystem/infosystemcache.cpp index c25fb10bb..8e4801540 100644 --- a/src/infosystem/infosystemcache.cpp +++ b/src/infosystem/infosystemcache.cpp @@ -24,11 +24,22 @@ void Tomahawk::InfoSystem::InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ) { qDebug() << Q_FUNC_INFO; - emit notInCache( criteria, caller, type, input, customData ); + if( !m_memCache.contains( type ) || !m_memCache[type].contains( criteria ) ) + { + emit notInCache( criteria, caller, type, input, customData ); + return; + } + + emit info( caller, type, input, m_memCache[type][criteria], customData ); } void Tomahawk::InfoSystem::InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output ) { qDebug() << Q_FUNC_INFO; + QHash< InfoCacheCriteria, QVariant > typecache; + if( m_memCache.contains( type ) ) + typecache = m_memCache[type]; + typecache[criteria] = output; + m_memCache[type] = typecache; } diff --git a/src/infosystem/infosystemcache.h b/src/infosystem/infosystemcache.h index 44476314a..ef31710c2 100644 --- a/src/infosystem/infosystemcache.h +++ b/src/infosystem/infosystemcache.h @@ -49,12 +49,13 @@ public: signals: void notInCache( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); - void finished( QString, Tomahawk::InfoSystem::InfoType ); public slots: void getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); void updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output ); +private: + QHash< InfoType, QHash< InfoCacheCriteria, QVariant > > m_memCache; }; } //namespace InfoSystem From 8729c91c95b922ac31b3f4257f9d16e67d76319c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 3 Apr 2011 13:07:20 +0200 Subject: [PATCH 56/63] * Fixed crash on startup with empty config. --- src/settingsdialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index b8808b621..72bc4edbe 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -87,7 +87,8 @@ SettingsDialog::SettingsDialog( QWidget *parent ) // MUSIC SCANNER //FIXME: MULTIPLECOLLECTIONDIRS - ui->lineEditMusicPath->setText( s->scannerPaths().first() ); + if ( s->scannerPaths().count() ) + ui->lineEditMusicPath->setText( s->scannerPaths().first() ); ui->checkBoxWatchForChanges->setChecked( s->watchForChanges() ); // LAST FM From f66a062796b958811c8dd067df196a2253902a89 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sun, 3 Apr 2011 13:30:13 -0400 Subject: [PATCH 57/63] Hopefully fix removing files actually being removed from db during watch scan --- src/musicscanner.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index dc0647c73..71707012e 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -75,9 +75,9 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) { // dont scan this dir, unchanged since last time. } - else if( mode != DirLister::MTimeOnly ) + else { - if( m_dirmtimes.contains( dir.absolutePath() ) ) + if( m_dirmtimes.contains( dir.absolutePath() ) || !m_recursive ) Database::instance()->enqueue( QSharedPointer( new DatabaseCommand_DeleteFiles( dir, SourceList::instance()->getLocal() ) ) ); dir.setFilter( QDir::Files | QDir::Readable | QDir::NoDotAndDotDot ); @@ -97,8 +97,6 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) qDebug() << "m_dirmtimes contains it? " << (m_dirmtimes.contains( di.absoluteFilePath() ) ? "true" : "false"); if( mode == DirLister::Recursive || !m_dirmtimes.contains( di.absoluteFilePath() ) ) scanDir( di.absoluteFilePath(), depth + 1, DirLister::Recursive ); - //else //should be the non-recursive case since the second test above should only happen with a new dir - // scanDir( di.absoluteFilePath(), depth + 1, DirLister::MTimeOnly ); } } From 74fd09235582103e0ae74dd988788b52b57efc49 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sun, 3 Apr 2011 13:50:52 -0400 Subject: [PATCH 58/63] Ok. Now the watched scanning is really fixed, in the sense that I can no longer break it. Until someone finds another way. --- src/musicscanner.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index 71707012e..b6c0ef617 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -33,7 +33,6 @@ void DirLister::go() { qDebug() << Q_FUNC_INFO; - qDebug() << "Current mtimes: " << m_dirmtimes; qDebug() << "Recursive? : " << (m_recursive ? "true" : "false"); if( !m_recursive ) { @@ -67,6 +66,13 @@ void DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) { qDebug() << "DirLister::scanDir scanning: " << dir.absolutePath() << " with mode " << mode; + + if( !dir.exists() ) + { + qDebug() << "Dir no longer exists, not scanning"; + return; + } + QFileInfoList dirs; const uint mtime = QFileInfo( dir.absolutePath() ).lastModified().toUTC().toTime_t(); m_newdirmtimes.insert( dir.absolutePath(), mtime ); From 5e2d196ba00251efccc7e92a5952966825f94dbb Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 10:36:07 +0200 Subject: [PATCH 59/63] * Fixed mtimes issue with windows paths. --- src/libtomahawk/database/databasecommand_dirmtimes.cpp | 10 ++++------ src/libtomahawk/database/databasecommand_dirmtimes.h | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.cpp b/src/libtomahawk/database/databasecommand_dirmtimes.cpp index 5b6034086..210f5e7e2 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.cpp +++ b/src/libtomahawk/database/databasecommand_dirmtimes.cpp @@ -36,11 +36,12 @@ DatabaseCommand_DirMtimes::exec( DatabaseImpl* dbi ) void DatabaseCommand_DirMtimes::execSelect( DatabaseImpl* dbi ) { - qDebug() << Q_FUNC_INFO << m_prefix << m_update; + QDir dir( m_prefix ); + qDebug() << Q_FUNC_INFO << dir.absolutePath() << m_update; QMap mtimes; TomahawkSqlQuery query = dbi->newquery(); - if( m_prefix.isEmpty() ) + if ( m_prefix.isEmpty() ) { query.exec( "SELECT name, mtime FROM dirs_scanned" ); } @@ -49,15 +50,12 @@ DatabaseCommand_DirMtimes::execSelect( DatabaseImpl* dbi ) query.prepare( QString( "SELECT name, mtime " "FROM dirs_scanned " "WHERE name LIKE :prefix" ) ); - query.bindValue( ":prefix", m_prefix + "%" ); - qDebug() << query.lastQuery(); + query.bindValue( ":prefix", dir.absolutePath() + "%" ); query.exec(); - qDebug() << query.lastQuery(); } while( query.next() ) { - qDebug() << query.value( 0 ).toString(); mtimes.insert( query.value( 0 ).toString(), query.value( 1 ).toUInt() ); } diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.h b/src/libtomahawk/database/databasecommand_dirmtimes.h index f35a78437..8b7c1c95d 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.h +++ b/src/libtomahawk/database/databasecommand_dirmtimes.h @@ -34,7 +34,7 @@ class DLLEXPORT DatabaseCommand_DirMtimes : public DatabaseCommand Q_OBJECT public: - explicit DatabaseCommand_DirMtimes( const QString& prefix = "", QObject* parent = 0 ) + explicit DatabaseCommand_DirMtimes( const QString& prefix = QString(), QObject* parent = 0 ) : DatabaseCommand( parent ), m_prefix( prefix ), m_update( false ) {} From 60b9d48d73e182d09be957180e1a16249859f503 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 10:48:48 +0200 Subject: [PATCH 60/63] * Updated Changelog. --- ChangeLog | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 3bcf6476b..03ede73fc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,5 @@ Version 0.0.3: + * Fixed an issue which caused duplicate items when rescanning. * Fix crashes in Twitter authentication. * Properly honor the chosen port number if a static host and port are marked as preferred. @@ -6,7 +7,7 @@ Version 0.0.3: speeds up importing sources a lot. * Faster painting of playlists with lots of unresolved tracks. * The tomahawk:// protocol handler works on Windows now. - * Fixed launching Tomahawk from Installer with administrative permissions. + * Fixed launching Tomahawk from Windows installer with admin privileges. * Prefer local results when results' score is equal. Version 0.0.2: From b08f4f1daff686f1497c1537a3598ae146a41d85 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 4 Apr 2011 05:18:22 -0400 Subject: [PATCH 61/63] Changelogify++ --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index 03ede73fc..88ee61dd1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ Version 0.0.3: * Fixed an issue which caused duplicate items when rescanning. + * Revert change introduced in 0.0.2 causing Twitter protocol to not try + to reconnect to a peer if it couldn't connect the first time the plugin + was connected. This caused confusing (and for most unwanted) behavior. * Fix crashes in Twitter authentication. * Properly honor the chosen port number if a static host and port are marked as preferred. From fdc2f5d3e9233439fc524f26cbe32bde3e0b7e23 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 4 Apr 2011 13:25:10 +0200 Subject: [PATCH 62/63] * Fixed thread afinity issue related to WebKit. --- src/resolvers/qtscriptresolver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/resolvers/qtscriptresolver.cpp b/src/resolvers/qtscriptresolver.cpp index 4a23439e6..3d1773fe8 100644 --- a/src/resolvers/qtscriptresolver.cpp +++ b/src/resolvers/qtscriptresolver.cpp @@ -68,6 +68,13 @@ QtScriptResolver::~QtScriptResolver() void QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) { + if ( QThread::currentThread() != thread() ) + { + qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; + QMetaObject::invokeMethod( this, "resolve", Qt::QueuedConnection, Q_ARG(Tomahawk::query_ptr, query) ); + return; + } + qDebug() << Q_FUNC_INFO << query->toString(); QString eval = QString( "resolve( '%1', '%2', '%3', '%4' );" ) .arg( query->id().replace( "'", "\\'" ) ) From 9cc74abcccee235e28caa0d28ec248e149ca5d2e Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Mon, 4 Apr 2011 14:21:47 -0400 Subject: [PATCH 63/63] Keep the dynamic playlist description up to date when it is changed --- src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp | 4 ++++ src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h | 3 +++ src/libtomahawk/playlist/playlistmanager.cpp | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp index 75fc46351..6f1f2302a 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp @@ -345,6 +345,8 @@ DynamicWidget::controlsChanged() return; m_playlist->createNewRevision(); m_seqRevLaunched++; + + emit descriptionChanged( m_playlist->generator()->sentenceSummary() ); } void @@ -356,6 +358,8 @@ DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control ) m_seqRevLaunched++; showPreview(); + + emit descriptionChanged( m_playlist->generator()->sentenceSummary() ); } void diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h index 441db1e13..1771fd07b 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h @@ -94,6 +94,9 @@ public slots: void playlistChanged( PlaylistInterface* ); void tracksAdded(); +signals: + void descriptionChanged( const QString& caption ); + private slots: void generate( int = -1 ); void tracksGenerated( const QList< Tomahawk::query_ptr>& queries ); diff --git a/src/libtomahawk/playlist/playlistmanager.cpp b/src/libtomahawk/playlist/playlistmanager.cpp index 31570a41f..2ba638c48 100644 --- a/src/libtomahawk/playlist/playlistmanager.cpp +++ b/src/libtomahawk/playlist/playlistmanager.cpp @@ -553,6 +553,13 @@ PlaylistManager::setPage( ViewPage* page, bool trackHistory ) if ( !AudioEngine::instance()->isPlaying() ) AudioEngine::instance()->setPlaylist( currentPlaylistInterface() ); + // UGH! + if( QObject* obj = dynamic_cast< QObject* >( currentPage() ) ) { +// qDebug() << SIGNAL( descriptionChanged( QString ) ) << QMetaObject::normalizedSignature( SIGNAL( descriptionChanged( QString ) ) ) << obj->metaObject()->indexOfSignal( QMetaObject::normalizedSignature( SIGNAL( descriptionChanged( QString ) ) ) ); +// if( obj->metaObject()->indexOfSignal( QMetaObject::normalizedSignature( SIGNAL( descriptionChanged( QString ) ) ) ) > -1 ) // if the signal exists (just to hide the qobject runtime warning...) + connect( obj, SIGNAL( descriptionChanged( QString ) ), m_infobar, SLOT( setDescription( QString ) ) ); + } + m_stack->setCurrentWidget( page->widget() ); updateView(); }