From 14a50616a37716fcd87921b0ea2d53b1d1adebcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9sir=C3=A9=20Werner=20Menrath?= Date: Sun, 10 Aug 2025 11:16:48 +0200 Subject: [PATCH] add dochstring in cfd_file_operations, cfd_navigation_manager, cfd_search_manager, cfd_settings_dialog and fix entryfield on create new folder and new document in iconview --- .../cfd_file_operations.cpython-312.pyc | Bin 18364 -> 21785 bytes .../cfd_navigation_manager.cpython-312.pyc | Bin 8233 -> 9482 bytes .../cfd_search_manager.cpython-312.pyc | Bin 21838 -> 23143 bytes .../cfd_settings_dialog.cpython-312.pyc | Bin 12562 -> 13162 bytes __pycache__/cfd_view_manager.cpython-312.pyc | Bin 37292 -> 38005 bytes cfd_file_operations.py | 109 +++++++++++++++++- cfd_navigation_manager.py | 33 ++++++ cfd_search_manager.py | 45 +++++++- cfd_settings_dialog.py | 16 +++ cfd_view_manager.py | 25 +++- 10 files changed, 220 insertions(+), 8 deletions(-) diff --git a/__pycache__/cfd_file_operations.cpython-312.pyc b/__pycache__/cfd_file_operations.cpython-312.pyc index 6b5dd502a75a8c9c46497e2ac2b00629d6cd0561..59aad8561a6812abefd8d14bc26d1ac5ab3c864f 100644 GIT binary patch delta 6479 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 Dm8#;* delta 3097 zcmb_eYj6|S72YfD%98c8EkCi1jFHXyApI0-6lI5n zVRO_SH6LcfVpKdVfS1)S_C)RY?1(yW)ERXmx}q*b_pC|I(+eTbUkGoq`V!$Z3&~8k zMB|b1fj{%I%<L00eX2W#wS8H!(Y`w*TGfKz?0P z_9_XD=fQG^zsOD&rl?l@_1(X?6UH2yTwXM()h6=jQ+Gl7qT_i}z95@JL#LT;i>u+$ z{2Cwc@(9;&t(wwc?hwk-JZLddi8Zmv`b6 zzLB5@VV>ee&)O=oK0+ZG6antWK{#Gil(ysnV*-3P|5TZ9Kzd%96{5!PG@BM?%ufl^ zOtyxg&l_s-;VW_;m+*(zBj}#+Zbb`ECF0LbDd9w9Lg9WItD!M*Pcojs%v|;QYnsX0 zLeNIAieNQCD}rp{w0YhIMTONx>+x2#AY>!Soix-9U1jAkR_JzZqT$aGQo_d_!SvS) z|7hA1`i$askNM=B2iCW$ljE4(8I9Kx#Wn&;6(2;HTk}c0*G2MNH{(=2XCyh5&=k(M z?sY(+QfT|+eVGD1rDleY-=mwlVL(}jyRGY`^+cqNW}wi1CC!@tT)tvVj(DksT8+Wb#SxDve`Qm%)TYoo~@d96ZF}R&ba*0(eL_aFE^1s+bX&H8#gmBzkFydVgyYRsmTctUd^36b=jIKrui?Au z`v|BTyBn6byo1jEqvZs8acEU_sUVv|x)dKtCRACsTj|k5RCON z%KZc-^OE_ba72VEj~AX@WQ8~SSEVnmtuw(}>kGiSejxpHhsbQ|IA3 z2&fg}QG*A~EERxP$kuEQnyERajKXc}+-adysr%qto&R1`cimHWVaa>7Z`a;lGjM&) zz?JqJYo5I3sk`Ob4sUj8-X`HvlP%a_zSP!L7pygZP%8x+pmpP(I-}s{Duq^WLBK|2 z_(XEb_95yOS2yl7oe{v_U4>~6c2}EZCrorNDX_#7s=~DgV$%SxcQ>sx#P|c^q%zi} zkyv~@%FmMF96=gErCnihTE`5v;4Q{pKggM@!c_v zmqV<-GB-0PI%!UH={NfCGHKW*Xl6JXEQiaFJ&oTKpY|7%2-CYZuV4p%hCKf_ZJj}~ z>`9vm@^)%))sCx$!sHDzSoINMNDM1KzH`y2m4 zjN||wEgw1Zj%_)z<(pfLLxTU3W}PQ^8v&zb2XV0e;IGN@8w6dN9FL4B{1h3!PjG?Y z9fCsy?-IO6a1jAJtf9;wlHm^omk2HsP+Iv12)f;D22bh~wn?0dTp^)r>O;0TKaTggo2kYM=HtWi&Bhb9A2q7 z(_oc$u{+*esXbF7N~<%DB~su{9}7s?PeOxQc4dorIkM|H+E^UghuoF`KJhvT4Gomt kq}zF)?pfF20W~$QbnuVi%+M<94l@g6gdJx1=TP1M0Nf4gGXMYp diff --git a/__pycache__/cfd_navigation_manager.cpython-312.pyc b/__pycache__/cfd_navigation_manager.cpython-312.pyc index d7fef1adae6507618866c40d9777d90a4179c599..19264f4b28c76c66fc9a541abc361293109bc79f 100644 GIT binary patch delta 2416 zcma)7O>7%Q6y90y`afyXwD}Ji3L;tJnnFcNTS^+(q@`aW zOHnHdZ!s$d7`Nhp2`d4ZY-mQxAIzC`yJQO6b>1)?v&4Awa5(U&Wph>#E^kn$vnWH$ zwuf^WY7ROUt(u}t%cf&hY^RjH_Ul}W7;5v~ByB&7jxB7nVmFybMVZkZpUWmYYZrLQ zJJNZ>02XX@OGoHA*tu%j4n0|ekFTD#E9_M86SW;rm`;IZxAqOwoI(6(xYJO5&101! zkIPx&4#Yt(F9mQ}dDyRvOl^Ed>V!n)WaK{J;ONMQBSUglyWWZ#F*8FHWh*Fs1y9&M z!dWJ2+@UM1L93j39&iYb7D&My0eTf}R%1{uEH`MF1F|d2%=$xg6jy-2XfGpHQ-F)^pL|)dkoVq7g#U8*j!8mBeKu@f9rpD_$B&j={=LlpFdNv0JblAEGqk^2_80Bqfig zo`==%q~>6JBXvnlkI5gB<8nUz4NUx`Xn~K_#+O{Jxxh`n!RawD?{8>FhVrF}UJBO!-zfgG2w$cGLL|2(8yA~u-v}hfC zZ<(TP8w`@6g)%Fwc(my9PChv^A@z)}+MWj?ybP^XyW2t|&v>*@;~dh{rImq_z={os zbfR@iwdM4bDma0&BDl5OW`ka!b(^jAoNK2c0!z<&f*WBCn#-PBsRQ=GQm=y@OScK|v>$b(LOz0=1qBPVeQ++6(D=rW1HWjkNF!os&4b$qmxCDJf9NX|&f(dj2!ek`B2 z+(JIj@$ThMBcML~WrRh9*AdWY>k7xsq4A?QLcoIX<^ZlJKWQg7wU|D&xqGjEfjr7S zrF&%a42kRWn|lJ#^qEI#T|X4Yf!#xj5epLK$J&<~KO^r>&ql)!$=|2vc3mLk(58YI GZ2Je7T8mi# delta 1133 zcma)*OK1~O6o&7eXEKk}nyNLXHMW5`)(0xuDx#_4qoM(qc2lQLY@;R7JEOHyUr=1Q z5ab|T_y7gL`dDajp$kFXC?dF&Q3O#0!9|U@a3h{GqqIv0=F8lF?m7RQb8gP>oG7MV z*|x#(y7^{&@L}p=>fD|#T#d6b>*XHnl{`MidSy>SDqhl4#(1ylskqWS4XJxNGT|kV z$r0Hx!p4$&tTNyRm0ESTTXhG@0`k10nN9GL!$ZxhxI?u8M+z0cJaj-L;WMwLG_>@S zh%@Z%-0b9j=@^HVat?mU&*1yK4)~&sE6mYKa9>-kIBAUfsy*NaT+z326F%uZDGK&= z9%X21p4^a-IkzW^Nk`%g4w{d60=^jondE%jcbQ#P@9KAOGhLjGuu*vw&dPJ(v2_Hq zF16Q4jD)%H&Pq$30T=9(k~trqS{W#$Uf`Cg)KhN3g9gCt+h$$wVJDSS$_VqK-(fz? zj>~)5Fn8=yXqJVjiN2r`43$MA8VUz-Ldq3RNQGROtk#0Q1GS@7Poz|t}gm!{V$P*U9t@KNi#u{g7fYGL`X41M9t~IsU^YGFC zC{eejq{-J!ACwYy!I4V>H6~UfoMdc5r$MYIbP_faHW7-1Zo+271Q+DzQ&V%ogyorn zwVH%W2%IXo8Nh-3%rr~8$Vw{3F2)R(b-49Eti*Q8`_Jfn%Q9;N3Ag_mJ#V?iZ5V0| zH=~b8pz3I1x}!N1V$Z0aR(dZ*-ySBa<>5YRv`<~gQ5nw{uCTV^!ry{8!`{R1h23BFOEIwUhksMgDK0tNo5?)4QlJ1HQHAPYa8L4g!@Ub`U5^>?RZu6HG)=(HWqB zn;@uo`vt@pt4myMscV*ckpIYLRG-(obaicgb_7{1{4f-?GtNPgjvlo%)PqA6t%}u( YJ#ZtxSc^^IO}?X{hx5)lBSv9=0IMPNivR!s diff --git a/__pycache__/cfd_search_manager.cpython-312.pyc b/__pycache__/cfd_search_manager.cpython-312.pyc index 81e4b12e76c97c94661ca6c4c86a7fab76731928..4a7e001d705af7b56b1c77a1a5cd16e1bf9ffa96 100644 GIT binary patch delta 4230 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 J3vS~s`yZflVb}lw delta 2684 zcmZuzeQZdViRs8jwWJu#om&RqC zElJ~w?$wngHtxXHfvbwEimMY>C$27BUAVe+H)_}DHHaSFgXldaYd*{W4I{^=W`}dd z+>F8DRhEuQ7ukr$ET^8&6-qOjY&lG$FwOmNLY@iuFa)y(VNrUwp&4$V@gVd%cCa9vaP+beT=6$muR8oP>w)*3J#fO+?DeA_4--V-hpst) zBZ_xlu5)h<(4qMX!lLvKZx9Z;m(dcdxvWS!gDW*}Ga0_``L-f2FzDVChL=4Lz+vwe zMbZ@5=IgT^)RTiSRD9cF5fU2+?s0`1R?wU+&BKk_^)TV^g^uO;S8&(-9m1mYfj0tf z|8H2Rdc_};2PMrplI4`7CGlolD(?P0=2Y%6V?YaVk(BKdk9a3RH$ex%7J@c{4e;IC zRFopJ9J6`dxHlwtqjqZ`iHGLT5U|*ZBzQy5WA@9TXCN1jLPsDB$LshVdDNK=u_jdh zv82ljGNSSm2iyp>3C)*-Nr$ff!U^5MIDG1F1rLT#v`U(L#PXYh3i;y9 z#B_&T?-^;D={|U^tvBvH(}=%h-M{2srfb57Ne`pBbB#|np8uCF zLH&ll`z-x_L%kdrfu8o8)p+t_8EG3z9c8DXlsXcpJGKJF(gZimlZCQL_2C1!S&@?& z&4aCRGu0mv6}dlq=uOAMdr_5B_ND_N7F%4z-k3}8XAAJ#^k?1>9aEfB60m(?c&Fp( zp7pdxel@4fs4uln=ghW@6_m*Avzzy_bMWEjEK;RC^EqpQ_RiL%xCOdFY-Z#+HZ?Qs z_sB2Bg9|S~p>u$phvm*nnu5|c+P-J#MRlrx{II-JCppI^pV0H1AEmb*g|V)ly4^yj zP?|S5w^;%|>WZ_CaJlP2HUXb@#h3=s?s!rpA{sG4uLNw?O+3a+Q-*13Q4tp33zOX! z+aDzjXWk@dG=_|##UAH#E>lT;;X6&uLy ztDfERx*zE>xO20d8G+@UEp^{S4VrcHMt)|ttSOj`wKLQ7IN6Zfx-9o}zF-t{bB4vR zXuJjDyXw0x;i;w0=k@Zex4>USNrd;2j#8Fi1bx?~y6yCmVia{g zg%WydskZHYUTJ9|?W+i@ELl;KI5x%mCT%yvhkMeyL~GfVrp@mmU@gkIvbY?}M{JzS zl_$)6#h`eEVE3oZMi|QWrm4^FpF>-559v!)B z1pcAZI18Q{T41;R1c%e>M37%n6l}u-!|Cnh%u@1seJvYuA)Ni0q`My7w>{eJ`0DUc z`KAQnk!NB={Yt2R_sMd(RQ$Suv^R~-TL(04trOwRk?%PFMAk0D*yyH)vnX1Q8ICTg zQ*L*~)1!^vYxMgqf*MUp^%kh;`)Im`wOy6!vvovlm!Kj?@NTwsSAp((8om=pO_~cXC-)HUn>kBET&)=L=QJNitGdr?j1)%hkNh~zjV0$ z5lTyQe}Scf05-?S&btKf5j;tnPP>@mw(s&%)Q^0bP6SaUEDfkf`hx$Z-6aGnNVpZg zcI31@mqoBXc>lOnz>bwk1=%@TiL60`qW8g8zoa( z&?ed*l=)Kxc%72vDj&}m%Q%EP;I)Sju5;5wx)aE%yML~vpDY*y*esjIGog8fO|n~( IScKaD0f%XCXaE2J diff --git a/__pycache__/cfd_settings_dialog.cpython-312.pyc b/__pycache__/cfd_settings_dialog.cpython-312.pyc index 8178e35517069cdfec56663ae74edb85fcfae2de..fd97dfbb9d406c77ca86ec097a0bc95c7104dee6 100644 GIT binary patch delta 867 zcmZuv%WD%s7@yfBedMu3MXHE&j0)M(Cb@Vp2omh2)>ddL!d3{Ao!QO6c4yh$B$`Vq z1ng0}U{&xao;+y$C!`k-UaU9uR4*b{dMP-Y&BK6mm zp4Ij)zm=n~T@cSlU>8uJLIPBYKs7>Q+dzjz>P)Lq66pXnMxtV$*ar8ZI#~wF#J*gn zp2sZ1U1ZqgM(b(`k=_k8nMHUL>D)mYw{&LI9TA7T8b#Q)O{QUwaSOR&zU1b*>+r+r z_AO-v&q4s=sK1sh=81}|McrQVIAAV53@kZ@TO7WEY#Te&@``9(V1{?WiWt<(jfI7T zE1R59)Xut|Gmp3(ki(`DCilL$-oxL-%o=FSX1GnQ3=$eME^g3_GCx*%py9t~v&OU< z!n#g1&qcU_nTe~4Ne7m=IkZT1TsOt9$NK;y=G_Z3KO%6on>_bbI*}ILa}TBfINAcg zPl9Q=dwl+8qAe+r*6SgovS1i`sMVdonITe8KWNOLQDJLzCdgomAkE}%KQr{|AJ^9% zW*F2F0??f$-~`sC_(EVgjh9f8PpvK@|Tr7T=iSZoAk>-&L^>L zfuAy%z1Y1n$0OI1N~%>{qhewNnrV*MhiHSQgj$$`Na!AFV6!fO{1qkZe_Tiij4ul> zlJ5eH2{CC~;CuIBE)C(7zmvZ-xj$K{5U%;}^QE(AM9fhKe2(5Yj4n&DYroX!so2b) gxrCo6Jcm#HSA~LXOYk;)ytcdXWaALn{)oCT9`<46}kTNz#kg_|Q&l(6YGO|y;XQ(on+sIx_ z6e#zjh2blcGZU-LCodL0R`G8NT$7WHW-?Bl{MX2UFM)~CpK(Ia^yrDvUl|f6TN~>G E0Bb!-&;S4c diff --git a/__pycache__/cfd_view_manager.cpython-312.pyc b/__pycache__/cfd_view_manager.cpython-312.pyc index ff75bf8119438938c5380b03f6609e16f6fb7df1..0e423a8ede29369e13d7c4b230a841389478db66 100644 GIT binary patch delta 1832 zcmah}X>3$g6u$Spc{B57FEi85OqXek(3R5KZE9Oe?Gjo#rL;pArD?%ok&&s)OsU8l zn#$6}N?VVKL@)s%DoY6XP>3d?NP;L!OVKV6txNbJic<=Sm}tCrN+BkQ_s93nJNG;1 zp7Wji-jV&x-S-*I*J`x_k!5SzwYIVDxF*lS4bWneD_jgho|?oTdQ%`l1VAjSx0ro9}Kbk+~gSuL0bJ| zk_~I@lLn*;zsso9MK}u*$;=6SN!*piV>~FnldZ(~ig?uKV(=c(lH18D zj}R3*QK#Zf)D7)Sqz3AVn%YynJzL;SqJd+;VSqx~jGEigSY zl)o?i(oN#ZML#iE4yB7f#j}B{_*+Re_sB3p2}-R%}ZF-k^dvmUhg+PVDD=YEn>e39KMFOs*we#i*ZYyoQip3dcJW z<$}~qoPHU^bT5DaBbRt*2{PDMV1%PPc^Ir-p_a`=`sF?YOcq4IPPaiP$PW`)=%Pw{ zBS^9^I4IGByG8*Y)XY&o+*kN!!$47lL{O}o3#Vlkh;kU!?1ONnz{5gEAxxz3aLZxU zaQu@*HT#y{3Ce<1sI!&MK zlsb8*LXho7Z!>~Q&HF_7 zaJn`=QcIe?uOdcHm7jBOBsrL1jt!5?VbsoaI~~{0^nNEHe~ndU_^Xa*1+E5U^|4yE zSJH-FVjEB!PEc2tLUw&Uc0gbKO{{~>4Hk=qDDL@{l<)bCf{ns03ej+=A&t`!Ga9~c zD3nGMqW3(^eWuc&H%%BVU5e`=q5YfpmJNrbOcBn{xm(!)V-opiYq0#PVex3poPly^ zT3%H}tp4(Qf&6IR94N*pCA&+~r|ecccT*gmjRbt}lk3DyLC36vzcdo@xl)Zo_9_$@YMB8TD2}$qpMnM;uY&o*j2sTnz&~ z#rT5wZO=^xkBetsTZW0x)R&8Y1V`UjmdXbSYpGRFp^3s8u=O{Y3o(~DRmv>FoOQ~< j*tpOsA5P{hx0l1Q{zN)mR%J_YM-j=>71^x#yh! zdEA*_Mup?A3bxO}!@~$W1=B}%UJH!d^4vnTL@V8wOfZNMWlnr6!HGqQ$+*2NiuNY{ zs=xv4^qqicOiq0V=Tb+&Mfap#gqU+~|BTyz-s(BpIvP9~Tol1m4FmRTGMz{-Rbdjv z?91>mozGSQKA~50EC6Te@BUo^9H#Ag{i5ZxN=QHHSC5cEd{l_TeeO4~Gg_sSg@q<+KnW?hx3qx9Y$ph2ta(keE7o&L%8ZgSYCCD0 zRW{+ptsddLjGY4+s5-2en-PY2fu1DN9nMt7(M8=fCM?+{WLRi9M@UG*Gwmz!=YYd3 z#jsFFHty4&D7UNy()(kUFnKl zl5cl_jFt82JYWi`-K+8Il2}1kv9{E~o=m~h^-jAm##ZGg6s9X$uA#hD@5MJqR2*(d zu$%ZmZBi&aWDQw@0VBS*!6C|RG7dDPfEnLzxShRn(t2J~m5e+iYigSV)3-U&q&B5Z z)#Vq+i-K;^!*#13)-E0o8yM(VjPS6OCT5;o7Dw^fWmPYcpeE3XE&LEBfYwKVFSlCHfw#$%DeLJ!w=Q`F?ThHO= z6FE6Dv8y8)((q_UG1SmY9UDaJ8lF+bgE@e&Y+Q+1K@V)i+F$`R)8XJpqC1mkxOs+$ zBbFnM;||=dRX{s^Py5`oiVaG%A_kvJ;Sm?D9ds({i_w%A3~WH~l#OFz(s2?kcV_3f6I}f#Y6|b_8sV@k?2Y`=A`8gvEf6DV4)w2Y99I Z>jB)4lhBC;hrRYnGo)W9oLVFs>TlMXe&PTC diff --git a/cfd_file_operations.py b/cfd_file_operations.py index f8e2d43..e8fc20b 100644 --- a/cfd_file_operations.py +++ b/cfd_file_operations.py @@ -14,11 +14,28 @@ from cfd_app_config import LocaleStrings, _ class FileOperationsManager: + """Manages file operations like delete, create, and rename.""" + def __init__(self, dialog): + """ + Initializes the FileOperationsManager. + + Args: + dialog: The main CustomFileDialog instance. + """ self.dialog = dialog def delete_selected_item(self, event=None): - """Deletes or moves the selected item to trash based on settings.""" + """ + Deletes the selected item or moves it to the trash. + + This method checks user settings to determine whether to move the item + to the system's trash (if available) or delete it permanently. + It also handles the confirmation dialog based on user preferences. + + Args: + event: The event that triggered the deletion (optional). + """ if not self.dialog.selected_file or not os.path.exists(self.dialog.selected_file): return @@ -61,12 +78,22 @@ class FileOperationsManager: ).show() def create_new_folder(self): + """Creates a new folder in the current directory.""" self._create_new_item(is_folder=True) def create_new_file(self): + """Creates a new empty file in the current directory.""" self._create_new_item(is_folder=False) def _create_new_item(self, is_folder): + """ + Internal helper to create a new file or folder. + + It generates a unique name and creates the item, then refreshes the view. + + Args: + is_folder (bool): True to create a folder, False to create a file. + """ base_name = LocaleStrings.FILE["new_folder_title"] if is_folder else LocaleStrings.FILE["new_document_txt"] new_name = self._get_unique_name(base_name) new_path = os.path.join(self.dialog.current_dir, new_name) @@ -82,6 +109,18 @@ class FileOperationsManager: text=f"{LocaleStrings.FILE['error_creating']}: {e}") def _get_unique_name(self, base_name): + """ + Generates a unique name for a file or folder. + + If a file or folder with `base_name` already exists, it appends + a counter (e.g., "New Folder 2") until a unique name is found. + + Args: + base_name (str): The initial name for the item. + + Returns: + str: A unique name for the item in the current directory. + """ name, ext = os.path.splitext(base_name) counter = 1 new_name = base_name @@ -91,12 +130,28 @@ class FileOperationsManager: return new_name def _copy_to_clipboard(self, data): + """ + Copies the given data to the system clipboard. + + Args: + data (str): The text to be copied. + """ self.dialog.clipboard_clear() self.dialog.clipboard_append(data) self.dialog.widget_manager.search_status_label.config( text=f"'{self.dialog.shorten_text(data, 50)}' {LocaleStrings.FILE['copied_to_clipboard']}") def _show_context_menu(self, event, item_path): + """ + Displays a context menu for the selected item. + + Args: + event: The mouse event that triggered the menu. + item_path (str): The full path to the item. + + Returns: + str: "break" to prevent further event propagation. + """ if not item_path: return "break" @@ -119,6 +174,15 @@ class FileOperationsManager: return "break" def _open_file_location_from_context(self, file_path): + """ + Navigates to the location of the given file path. + + This is used by the context menu to jump to a file's directory, + which is especially useful when in search mode. + + Args: + file_path (str): The full path to the file. + """ directory = os.path.dirname(file_path) filename = os.path.basename(file_path) @@ -129,6 +193,17 @@ class FileOperationsManager: self.dialog.after(100, lambda: self.dialog.view_manager._select_file_in_view(filename)) def on_rename_request(self, event, item_path=None, item_frame=None): + """ + Handles the initial request to rename an item. + + This method is triggered by an event (e.g., F2 key press) and + initiates the renaming process based on the current view mode. + + Args: + event: The event that triggered the rename. + item_path (str, optional): The path of the item in icon view. + item_frame (tk.Widget, optional): The frame of the item in icon view. + """ if self.dialog.view_mode.get() == "list": if not self.dialog.tree.selection(): return @@ -141,16 +216,36 @@ class FileOperationsManager: self.start_rename(item_frame, item_path) def start_rename(self, item_widget, item_path): + """ + Starts the renaming UI for an item. + + Dispatches to the appropriate method based on the current view mode. + + Args: + item_widget: The widget representing the item (item_id for list view, + item_frame for icon view). + item_path (str): The full path to the item being renamed. + """ if self.dialog.view_mode.get() == "icons": self._start_rename_icon_view(item_widget, item_path) else: # list view self._start_rename_list_view(item_widget) # item_widget is item_id def _start_rename_icon_view(self, item_frame, item_path): + """ + Initiates the in-place rename UI for an item in icon view. + + It replaces the item's label with an Entry widget. + + Args: + item_frame (tk.Widget): The frame containing the item's icon and label. + item_path (str): The full path to the item. + """ for child in item_frame.winfo_children(): child.destroy() entry = ttk.Entry(item_frame) + entry.pack(fill="both", expand=True, padx=2, pady=20) entry.insert(0, os.path.basename(item_path)) entry.select_range(0, tk.END) entry.focus_set() @@ -173,7 +268,7 @@ class FileOperationsManager: text=f"{LocaleStrings.FILE['error_renaming']}: {e}") self.dialog.view_manager.populate_files() else: - self.dialog.populate_files(item_to_select=os.path.basename(item_path)) + self.dialog.view_manager.populate_files(item_to_select=os.path.basename(item_path)) def cancel_rename(event): self.dialog.view_manager.populate_files() @@ -183,6 +278,14 @@ class FileOperationsManager: entry.bind("", cancel_rename) def _start_rename_list_view(self, item_id): + """ + Initiates the in-place rename UI for an item in list view. + + It places an Entry widget over the Treeview item's cell. + + Args: + item_id: The ID of the treeview item to be renamed. + """ self.dialog.tree.see(item_id) self.dialog.tree.update_idletasks() @@ -220,7 +323,7 @@ class FileOperationsManager: text=f"{LocaleStrings.FILE['error_renaming']}: {e}") self.dialog.view_manager.populate_files() else: - self.dialog.populate_files(item_to_select=item_text) + self.dialog.view_manager.populate_files(item_to_select=item_text) entry.destroy() def cancel_rename(event): diff --git a/cfd_navigation_manager.py b/cfd_navigation_manager.py index fe2b659..0ead7aa 100644 --- a/cfd_navigation_manager.py +++ b/cfd_navigation_manager.py @@ -3,10 +3,27 @@ import tkinter as tk from cfd_app_config import LocaleStrings, _ class NavigationManager: + """Manages directory navigation, history, and path handling.""" + def __init__(self, dialog): + """ + Initializes the NavigationManager. + + Args: + dialog: The main CustomFileDialog instance. + """ self.dialog = dialog def handle_path_entry_return(self, event): + """ + Handles the Return key press in the path entry field. + + It attempts to navigate to the entered path. If the path is a file, + it navigates to the containing directory and selects the file. + + Args: + event: The tkinter event that triggered this handler. + """ path_text = self.dialog.widget_manager.path_entry.get().strip() potential_path = os.path.realpath(os.path.expanduser(path_text)) @@ -21,6 +38,18 @@ class NavigationManager: text=f"{LocaleStrings.CFD['path_not_found']}: {self.dialog.shorten_text(path_text, 50)}") def navigate_to(self, path, file_to_select=None): + """ + Navigates to a specified directory path. + + This is the core navigation method. It validates the path, checks for + read permissions, updates the dialog's current directory, manages the + navigation history, and refreshes the file view. + + Args: + path (str): The absolute path to navigate to. + file_to_select (str, optional): If provided, this filename will be + selected after navigation. Defaults to None. + """ try: real_path = os.path.realpath( os.path.abspath(os.path.expanduser(path))) @@ -49,6 +78,7 @@ class NavigationManager: self.dialog.widget_manager.search_status_label.config(text=f"{LocaleStrings.CFD['error_title']}: {e}") def go_back(self): + """Navigates to the previous directory in the history.""" if self.dialog.history_pos > 0: self.dialog.history_pos -= 1 self.dialog.current_dir = self.dialog.history[self.dialog.history_pos] @@ -58,6 +88,7 @@ class NavigationManager: self.dialog.update_action_buttons_state() def go_forward(self): + """Navigates to the next directory in the history.""" if self.dialog.history_pos < len(self.dialog.history) - 1: self.dialog.history_pos += 1 self.dialog.current_dir = self.dialog.history[self.dialog.history_pos] @@ -67,11 +98,13 @@ class NavigationManager: self.dialog.update_action_buttons_state() def go_up_level(self): + """Navigates to the parent directory of the current directory.""" new_path = os.path.dirname(self.dialog.current_dir) if new_path != self.dialog.current_dir: self.navigate_to(new_path) def update_nav_buttons(self): + """Updates the state of the back and forward navigation buttons.""" self.dialog.widget_manager.back_button.config( state=tk.NORMAL if self.dialog.history_pos > 0 else tk.DISABLED) self.dialog.widget_manager.forward_button.config(state=tk.NORMAL if self.dialog.history_pos < len( diff --git a/cfd_search_manager.py b/cfd_search_manager.py index 838fc7c..c35f283 100644 --- a/cfd_search_manager.py +++ b/cfd_search_manager.py @@ -9,7 +9,15 @@ from cfd_ui_setup import get_xdg_user_dir from cfd_app_config import LocaleStrings, _ class SearchManager: + """Manages the file search functionality, including UI and threading.""" + def __init__(self, dialog): + """ + Initializes the SearchManager. + + Args: + dialog: The main CustomFileDialog instance. + """ self.dialog = dialog def show_search_ready(self, event=None): @@ -18,7 +26,11 @@ class SearchManager: self.dialog.widget_manager.search_animation.show_full_circle() def activate_search(self, event=None): - """Activates the search entry or cancels an ongoing search.""" + """ + Activates the search entry or cancels an ongoing search. + + If a search is running, it cancels it. Otherwise, it executes a new search. + """ if self.dialog.widget_manager.search_animation.running: if self.dialog.search_thread and self.dialog.search_thread.is_alive(): self.dialog.search_thread.cancelled = True @@ -28,6 +40,12 @@ class SearchManager: self.execute_search() def show_search_bar(self, event=None): + """ + Activates search mode and displays the search bar upon user typing. + + Args: + event: The key press event that triggered the search. + """ if isinstance(event.widget, (ttk.Entry, tk.Entry)) or not event.char.strip(): return self.dialog.search_mode = True @@ -37,6 +55,9 @@ class SearchManager: self.dialog.widget_manager.search_animation.show_full_circle() def hide_search_bar(self, event=None): + """ + Deactivates search mode, clears the search bar, and restores the file view. + """ self.dialog.search_mode = False self.dialog.widget_manager.filename_entry.delete(0, tk.END) self.dialog.widget_manager.search_status_label.config(text="") @@ -45,6 +66,11 @@ class SearchManager: self.dialog.widget_manager.search_animation.hide() def execute_search(self, event=None): + """ + Initiates a file search in a background thread. + + Prevents starting a new search if one is already running. + """ if self.dialog.search_thread and self.dialog.search_thread.is_alive(): return search_term = self.dialog.widget_manager.filename_entry.get().strip() @@ -58,6 +84,16 @@ class SearchManager: self.dialog.search_thread.start() def _perform_search_in_thread(self, search_term): + """ + Performs the actual file search in a background thread. + + Searches the current directory and relevant XDG user directories. + Handles recursive/non-recursive and hidden/non-hidden file searches. + Updates the UI with results upon completion. + + Args: + search_term (str): The term to search for. + """ self.dialog.search_results.clear() search_dirs = [self.dialog.current_dir] home_dir = os.path.expanduser("~") @@ -144,6 +180,7 @@ class SearchManager: self.dialog.search_process = None def show_search_results_treeview(self): + """Displays the search results in a dedicated Treeview.""" for widget in self.dialog.widget_manager.file_list_frame.winfo_children(): widget.destroy() @@ -244,6 +281,12 @@ class SearchManager: search_tree.bind("", show_context_menu) def _open_file_location(self, search_tree): + """ + Navigates to the directory of the selected item in the search results. + + Args: + search_tree: The Treeview widget containing the search results. + """ selection = search_tree.selection() if not selection: return diff --git a/cfd_settings_dialog.py b/cfd_settings_dialog.py index faa50e8..ce61170 100644 --- a/cfd_settings_dialog.py +++ b/cfd_settings_dialog.py @@ -11,7 +11,17 @@ except ImportError: class SettingsDialog(tk.Toplevel): + """A dialog window for configuring the application settings.""" + def __init__(self, parent, dialog_mode="save"): + """ + Initializes the SettingsDialog. + + Args: + parent: The parent widget. + dialog_mode (str, optional): The mode of the main dialog ("open" or "save"), + which affects available settings. Defaults to "save". + """ super().__init__(parent) self.transient(parent) self.grab_set() @@ -145,6 +155,11 @@ class SettingsDialog(tk.Toplevel): command=self.destroy).pack(side="right") def save_settings(self): + """ + Saves the current settings to the configuration file and closes the dialog. + + Triggers a UI rebuild in the parent dialog to apply the changes. + """ new_settings = { "button_box_pos": self.button_box_pos.get(), "window_size_preset": self.window_size_preset.get(), @@ -161,6 +176,7 @@ class SettingsDialog(tk.Toplevel): self.destroy() def reset_to_defaults(self): + """Resets all settings in the dialog to their default values.""" defaults = CfdConfigManager._default_settings self.button_box_pos.set(defaults["button_box_pos"]) self.window_size_preset.set(defaults["window_size_preset"]) diff --git a/cfd_view_manager.py b/cfd_view_manager.py index b316393..c408889 100644 --- a/cfd_view_manager.py +++ b/cfd_view_manager.py @@ -125,8 +125,16 @@ class ViewManager: ttk.Label(container_frame, text=error).pack(pady=20) return - widget_to_focus = self._load_more_items_icon_view( - container_frame, _on_mouse_wheel, item_to_rename, item_to_select) + widget_to_focus = None + while self.dialog.currently_loaded_count < len(self.dialog.all_items): + widget_to_focus = self._load_more_items_icon_view( + container_frame, _on_mouse_wheel, item_to_rename, item_to_select) + + if widget_to_focus: + break + + if not (item_to_rename or item_to_select): + break if widget_to_focus: def scroll_to_widget(): @@ -270,7 +278,12 @@ class ViewManager: self.dialog.tree.insert("", "end", text=error, values=()) return - self._load_more_items_list_view(item_to_rename, item_to_select) + while self.dialog.currently_loaded_count < len(self.dialog.all_items): + item_found = self._load_more_items_list_view(item_to_rename, item_to_select) + if item_found: + break + if not (item_to_rename or item_to_select): + break def _load_more_items_list_view(self, item_to_rename=None, item_to_select=None): start_index = self.dialog.currently_loaded_count @@ -278,8 +291,9 @@ class ViewManager: self.dialog.items_to_load_per_batch) if start_index >= end_index: - return + return False + item_found = False for i in range(start_index, end_index): name = self.dialog.all_items[i] if not self.dialog.show_hidden_files.get() and name.startswith('.'): @@ -305,14 +319,17 @@ class ViewManager: self.dialog.tree.focus(item_id) self.dialog.tree.see(item_id) self.dialog.file_op_manager.start_rename(item_id, path) + item_found = True elif name == item_to_select: self.dialog.tree.selection_set(item_id) self.dialog.tree.focus(item_id) self.dialog.tree.see(item_id) + item_found = True except (FileNotFoundError, PermissionError): continue self.dialog.currently_loaded_count = end_index + return item_found def on_item_select(self, path, item_frame): if hasattr(self.dialog, 'selected_item_frame') and self.dialog.selected_item_frame.winfo_exists():