From d9ae449f0424c7d296a2fd1372d3393b5c7c0152 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Mon, 15 Feb 2021 17:28:16 -0500 Subject: [PATCH] Improve Auto-Type Select Dialog Significant improvements to the Auto-Type select dialog. Reduce stale and unnecessary code paths. * Close select dialog when databases are locked. * Close open modal dialogs prior to showing the Auto-Type select dialog to prevent interference. * Never perform Auto-Type on the KeePassXC window. * Only filter match list based on Group, Title, and Username column data (ie, ignore sequence column) * Always show the sequence column (revert feature) * Show selection dialog if there are no matches to allow for a database search * Close #3630 - Allow typing {USERNAME} and {PASSWORD} from selection dialog (right-click menu). * Close #429 - Ability to search open databases for an entry from the Auto-Type selection dialog. * Fix #5361 - Default size of selection dialog doesn't cut off matches --- docs/images/autotype_selection_dialog.png | Bin 32857 -> 27332 bytes .../autotype_selection_dialog_search.png | Bin 0 -> 31823 bytes .../autotype_selection_dialog_type_menu.png | Bin 0 -> 36766 bytes docs/topics/AutoType.adoc | 14 +- docs/topics/DownloadInstall.adoc | 2 + docs/topics/Welcome.adoc | 2 + src/CMakeLists.txt | 8 +- src/autotype/AutoType.cpp | 204 ++------- src/autotype/AutoType.h | 9 +- src/autotype/AutoTypeFilterLineEdit.cpp | 39 -- src/autotype/AutoTypeFilterLineEdit.h | 42 -- src/{core => autotype}/AutoTypeMatch.h | 27 +- .../entry => autotype}/AutoTypeMatchModel.cpp | 25 +- .../entry => autotype}/AutoTypeMatchModel.h | 2 +- src/autotype/AutoTypeMatchView.cpp | 120 +++++ .../entry => autotype}/AutoTypeMatchView.h | 25 +- src/autotype/AutoTypeSelectDialog.cpp | 420 ++++++++++++------ src/autotype/AutoTypeSelectDialog.h | 47 +- src/autotype/AutoTypeSelectDialog.ui | 197 ++++++++ src/autotype/AutoTypeSelectView.cpp | 62 --- src/autotype/AutoTypeSelectView.h | 41 -- src/autotype/WildcardMatcher.cpp | 96 ---- src/autotype/WildcardMatcher.h | 46 -- src/browser/BrowserService.cpp | 4 + src/core/AutoTypeMatch.cpp | 43 -- src/core/Entry.cpp | 70 +++ src/core/Entry.h | 1 + src/fdosecrets/objects/Service.cpp | 2 +- src/gui/DatabaseTabWidget.cpp | 8 +- src/gui/DatabaseTabWidget.h | 4 +- src/gui/MainWindow.cpp | 13 +- src/gui/MainWindow.h | 7 +- src/gui/SearchHelpWidget.ui | 2 +- src/gui/SearchWidget.cpp | 4 +- src/gui/entry/AutoTypeMatchView.cpp | 154 ------- tests/CMakeLists.txt | 3 - tests/TestWildcardMatcher.cpp | 88 ---- tests/TestWildcardMatcher.h | 46 -- 38 files changed, 830 insertions(+), 1047 deletions(-) create mode 100644 docs/images/autotype_selection_dialog_search.png create mode 100644 docs/images/autotype_selection_dialog_type_menu.png delete mode 100644 src/autotype/AutoTypeFilterLineEdit.cpp delete mode 100644 src/autotype/AutoTypeFilterLineEdit.h rename src/{core => autotype}/AutoTypeMatch.h (56%) rename src/{gui/entry => autotype}/AutoTypeMatchModel.cpp (88%) rename src/{gui/entry => autotype}/AutoTypeMatchModel.h (98%) create mode 100644 src/autotype/AutoTypeMatchView.cpp rename src/{gui/entry => autotype}/AutoTypeMatchView.h (72%) create mode 100644 src/autotype/AutoTypeSelectDialog.ui delete mode 100644 src/autotype/AutoTypeSelectView.cpp delete mode 100644 src/autotype/AutoTypeSelectView.h delete mode 100644 src/autotype/WildcardMatcher.cpp delete mode 100644 src/autotype/WildcardMatcher.h delete mode 100644 src/core/AutoTypeMatch.cpp delete mode 100644 src/gui/entry/AutoTypeMatchView.cpp delete mode 100644 tests/TestWildcardMatcher.cpp delete mode 100644 tests/TestWildcardMatcher.h diff --git a/docs/images/autotype_selection_dialog.png b/docs/images/autotype_selection_dialog.png index 6f15a6ea68f0cc9fc40b391e2a4a0acf2cc76a2c..ce94f6cfa9a033b748177074c81d57149a94bf92 100644 GIT binary patch literal 27332 zcmbrkWmKF&(qnhQVDD2r`4ay9f8+g9aN265Mrw;0zLUfDkN% z<$2%l?C$w~?VkPDU47Tp)z#J4-BqV!v@{e6@u=`nP*4b!mE?3#P%vO9DCj&m=+Ba+ zXp>+R6tqVzH9h&eySt~Sr;9&lH#axhADkxvMekb2%J}G8f;l>kMy6r1iy6u%+gTHT61S%ySkaX_|wGlxhV~P zc7G3N(MV-s@tFH_)_il;{F!YoL*t^&L!E`qz3ogrW99;$DiCv)I%MHa^V;1bVEOKj zmR8M4mTmLt$)o+*u&s)oKowg0PWRVNUlUo(+@996Sy;rrCaGtsG101Ir}~I?c(Ofp zdFrvSJw3rRSlB#S*eZJ5?pCr{G_rr@=il9R2KFRM_yl@%J=xj0>Ln%8(s~A(v9ZX4 zx}Kg=dlFe#ygTmhB%Zatg6=xJO--#*6ezxQWXV!doSmPF#vRpR5~XGq-`uoPD5U6S z&895fP*BMyG?fNQQ7kO>mSYfoK_|>x*tl6rY1(^STr8oWa_t`&XuY|iaBFQ_-1Ki> z^RE47PDMdMWo=nuCYQ9Wog~T?mxklpCZD=Q5>)5z@x`S^`&03(a{i=h&S&$yarY0- z6jV|aQeo!#5+j+;OR4gHP4Bf#3qxw56c#1zK~i*)MK`Ukai|35`Rm#rBA@mdq#nCV;Ji|%(q z9N%3m%pR)gP=@uN6cmO=L?^U^o0?jH;vy`X8Z#c=c6JW5N~)9F{cY_YPx~`z88M$d zW&iCToOg8vG!(Vn4L8r_pC%+7@QaUSM1dv!~NnkJs+$Lt6TJvTPC-*57gn z(@{|DLzU%Z^uVhp`Q5Rvv`L2#ejJkTyu!K1B4>!uEKZ0hqo8Oo2={dK#J5CWJuOKd zlwB(_o^eg3S7Qig859&_?@w;%50$~s4M3ZIytJ7hQvPej{V(WvD(_347$5n=?wXhn zYo9F&3Nm@igAxS=hJJ~L0>(vPq992|@KLr{k^v~as`NmV;4Enn3JNgf`J{r-AgCxX zjQ`(r>qV<;$E;!|0MuHtyguM&r504PP^JWl8;8-0mz0L2vtMIQ(y zxZk}1HiWLj@7o?QaL5Y;C%;aNujV=fH+gkE`L-$+o@Je0aRH!)+N( zZ7{629-d>z^asv0i}MGNuX{<+Rgz$b6vhol{fysnXH7~&N}?0 zX|OiX)6eoIyF@E8if&{!VT2AO;{yk|m|0t>NndBux}N&cq2+~GMjWcWfo;9Y z^Uv)45TcXU?SLY!ZTa!nrv9El`NHV2m0J38LVCq#{lx09WfEhEL~LXlkXUS&O1($n zT-k{}1pxf%9Tw!HYOmd0@?xRnc6>v-HJ20GM&>83oS`*7evJn)3Sz3h`Zakl9qEcY z@hPj-fGz@nBF~B^_`}S3oKvakvZ;Sga&&C`dc{cPg^hza6VTLy@*^_Q$?)F2@H9%~ z^4_Kk{6!Bp1GXmpcV5@jK>5uamPVkz0-Oj%>w{n@A*g=Ql@v0YuhyomR+JTY$vc=- zX1ljX)!^hpYRJgUh7ftMTBFLRyHlTOd^3g5IxdW`kFMKBA@j}5(A z|7Z6&N1Sr@R^t!E6ivRcT@@FCs^z&vdnHQ1zM?|`iAYix7t$}$%$)LotS4swdX~9vN4^%u&t9pA?5CRJnVe zMM>Hj9_iA$QBVeL@*$v0u$C3Vi|SPXKLh4&ZFpY8T0&RG5G*hHY$fIXn{KjGvl;{c30Em*ektU2LQC2v^8m|eR%tiy1}$h4!6V<|YtNnfO>B1NWU`e9NWnDyq;169#C zKuaGo#9E=wXAV*;6nzc|dC>iDe|h`PuarSCN57@fT;H;H))T8Q=w@XjF{K-gZt6%b zdHZ!rPRrLb(h;`dI$9kCE8h8-4bU48F1{0ps9z#%Av$fHVc_tmADLs?8(^lsAGdyg zJnWmX^5XPbIMY=lMukDz0rR!Z-!$l<1lkrI1RA1|mX*W&*bEIJpI#f77eNXBet}5? z*x^qA{=EZkZEgGhhf3EC3#+=4|HG%v!2ER@!VYBUGnp0ytOuz|^r1gfs(%^)1?8D# zgP$n=FR0hw{wt9Z3W^0iPUFAw8v!Ez@MLBny(5HHIuRDk3_(@CL`y|rGLPabr2_sl zq70Hw{l6Ljdmja3Jm1WuXP)4vIG*8LqxeRtfCgrug)*r38Hxphs@iCc#Yxcrj$+8K zp7ZoQL1a8R-m9@G%&4UH=X=+g50~09o|j7g9Z@VrZsc524&CTDtv6XIdtkKcAdRN0 zAz4)00{jNRtm;l$qrY8u1a+n~$TkNdaR{?J5D_$u{t$@j%BAS}D5Bh-I0&RtRKaZk z%F)BkB!*;Ys(_^HOhNwyPnQu}h2m%dB=QbjwtIu+TnxbVh2}B*agNJX{!IiXgN&7W z_s*sQDR0PCV1M2RKbb#y+V4$r?#d^)fFI{Cb6~ZMP}j6fKq9IFBBLt`S|$fVX9QX* zLqeoo-&~H#q(S_-FVUVQju3a}F?VdWv~4lO-t9V{xRxf3P{Zt_0zuF2NJS(pQjyQ8 zXV6FASM}(63R(YN3LEl?nlU-`K+3yP!Ocz3Qat{X{lQ=TdaO{k#O^ukIukj|tUefHB$d*dH7k zyZxY2UmUR%@kANdjd_uLw5`8IJF2n@&fR!7!f zV934^d;^VwgbN6@nKB0#W6edeahno|Db>5UGpoE4U`!8vJ-~Gsyu@HF#Ct+8&w$C{@hz z%l@>X5bKhOI&(5hY1d??c5;iSpm9uSn>;8b{u%KVYRi(9E@~E zX{Cv0RJDHmfQGDbHvINMHFaoP=ZWi{GtVxpD%C1_d4%(<4JQ}|!jc~06YEf%tPmg= zu*B~~TL-zTdc4FLVCG`)d|>FuU49%rR{9A{hEvdWMDt(Hl^swi{wRB|lPs(g23u80 z^2%(tUZHB4Ou?>amOY-NHtAIOVQHaA|93DpjUt z-EAQvY&_H2rTOjdE3e;|zeS^}vb`L0r=*w0+GG>%Mrk|*eoD@g3l9lo0>_l5@0I{#Dwg-w0=!~~5Q>kc&zz#$VAg(M*2GUfA3 z$f8`v^m=g^{@5FawVC+t;F#MD1xp+)vXOeyDzUHhuIo7IPIo#n2jsyfu$iEudcq9y zo!fCduptK~sMU0^_j$3crT5!; zMBx2Vapl_*jI{z3a){|?WxjKf^7bBe7a`xT`}^DPD#Rc<393|~!qb^7osaITM7@VN z%3q4THu!POno8E?Zs~F(q;h!nYA3vZ z6$|DhutVslWcUl4aY~O7`#i0NX~!xJF^E?~0ygc8D1D|*%T@5$Zayrrt1cN3bs!J2 zztJhcNR1S@B$=wC9*DR*2YWxArvXMwa!p6#hR8&U(ymyc7u27g{1Ah;tXLjG!kd!`4-$QDnXG|g`jM4) zyCD$zaqD;w+WJS$KEAKo%PlnDX7gsHWC2bLNAPP#NUM@ZfaE9TCn+Ad7X|7R{G7`l zS%ES3`Bb%eR&Rj5Zp^^biehKV#87+0wY6tmO4hUMRt7>#F#c<9gHhENKXq4}lF#d~ z$-S&4FIDwY-gNkHPKjDy){&&p+TW~j$cU#Uv`I?&Tw3LZAg%??#dzw!D{tFSH`87A z=NdP$kghnHvM@Kxf@ZLzc+-kWB067afOHs>jO3cXmRbwpnzCfaOdh**yeL8!RaEa{PHhEM389i1ey+i2crb|B zbukUpXMA&}HKj;9Y%sa~z%AunqFFxYmYo4(lpbT%=cSa$2U3?uqhZI?$hDYlEe4)R zSkAwZq=s0Q7d?9zvD3&=a{*l$lfXBm%Y!WkG5~C5Yjf8snnJR zfG!!1jsrTCUxqxIwXpX`VP5t#ZC_nk{n9q<;&IL$<($lRqOAek>P*%g`3R51k%`t3 zV81^`44x-D@<>xBz{C%O?OcqsC&E@B(h*S)X>i<@r$_suhqT)_Ui-DXkBviDlunQ* z=-;AK!#`(xSe6vgC5NGyWHZ>0hEX9AhM9RqN>|yRIX)?jWsn?%f%Sz;xdlE70shUM z?9m-$8&)+cPG^BzdNLum&jWGuBQW+Mk zw1X)}0;Ep`qo(}8k7fo*Gykulaqv2-%1a>q+%|Z9ta*sPqR)M&_3Iv0mRcCv4`{0gm^EZ5^v${HdLhaAU~6O_fdYftw9aiH542S2GVYmgmM>L)}@n0 zs}@v}#EDCD%y0~0pr)-tjNr@Qt4lq*bO&_Q{|AA^284E)TsJ#eyMz#f!UqJ0fW*`+ zW&Zhn0s!~PeoK6E2@ z5+4;&=HZ`LXlH+nFByN8cq)HkbuHsJ{$o$UcgWn|2O+)!9PBQqEk70BVT9=uJ6IxD zZDqRl+WJZAFN~|Gt8rSmz_jP6(1%@DlkJPs`LJ)CWk{XJww*{rC#U?WTw^_(gjk3P zYA|nIwfdLBsqqlQFKzIw9GhmEI$Tgs?+jygAMEb8CPZ`1D0ae+_2@T-a=N zRYx8J`WV8uNe!W7WC%{s z9xLC#ji??g;h}LEvLA*JQ+*w>?I2f22QK@Gs)7SveCr3@KI6Qg`++ij-c)erh6z;s z$YSMehziu>yW4O<%k}+;0xf*4UQB$e9x{v zLelrgBz{t_FMjYx!Ja)_7CkyNqwjy&K^Mb8W%~nSafD3FC@Cd6;DqE|)3?O2j^Q)RwkE_4lFb1x|NX=d$!5sY={f|AP9L; zsD1l8+wzY2iHUb0)b2r$hS@Na4K{?^W*UQo`Fb~r7OZ?A@9>v&%(f%P6#DBNMgi(2 z)zC6m0&nAFzMus+N^YWn{Hp+Ia!d7(V!-9Ne?_z{nzwnFi4egUG6$Sx83QCfUj$qg zX=}^I&zs5j?2tG72n=^)?jwD58sz=(6eWV60A2gHTy4`xWHUXg;-Ms9i;4!b@jP?h z;!+^sTOM(JNwv3V(4wH@-rrI2lM4+0^dJvihMnDnPzeU-x8P!^SZ^Hn(%E+Fl8VbO zjcsj!?b$2*dKLRaLz^fkUY%a&LjIe#VX~}xNRB*UXhpBmS|%#gc`M>qTL^r7B@BMY zvK0{wm@hRRa0!Oj{_0iiJ+82#q(dHIf!Ikqx1GU|afK<%jU_##EYv61kjm-nulqmD zpC>haJH1fnSYf{j=|Bn!q{!7)4`w$1tv>vN@uHXz@`b1>uXPmA>Qm*md|v@csm_?7 zmLAC&JJ~<>Wlf3rMsid2zbwQd?N1WmUj%UmguprcYtqKP786aa5fm1z>4m2E-%){k z)5)%*`F*NgFke(ytU-)zz}GDb7&qOYbUaL0w!~4sVEueimvhF4Ln>>_Pk@RbF7(iLQ8?w2S-tI8)+g_FFYSV?{aAv(uad4K|O24>^eoz32iYjml@{>JfB{VDG`f()$AO#2jJqS`cK9~H% zxmmEzb-YQZ43Q-P*O$Eau=-Fy3Ej^Rkxrw|J8fE5D%kUlJ^z8@p4h@H|N44ff2G)R z?%o17S(9GqY6}5}Ac$0@r-%dDMT;M?>hVMI2>Z#MYW}nN_+QRPj40p zKE24qV~U^EZ`Wpjz3!&nd;GHMT|b3c1oam*&?z36mXS<>qO|4{lWuQ~87lOBSwD`o zCz+cK9=Mfr%PU%s!ZNWv8_O8IZ36!xv_aShsB6oSEbq_t=(K|B18gc@Jz z*3~UA0{yN#$hNst-6ri5h{$A(EKrK#U=V+ z@TuO0a1$2l59hC!Ln#6{^dPYep~PAll#A6W*#8c(*f~ zU&9K0_W^fDFrnRkM1UPl5SK!yET;DZ&WBh`A*}9@*yHsLF`7f-Ipu`h%638doZy<@ zj_4urcDy%YBly&a@#PDDdC~+QcumUw)!5fA-E3>p=}7CTv^U&P{wa?{ZE@;E5pJm7 z|6>z7(~!Q2$wCXSn2V=ThMx>*IM}?M{aF|i;i`L}w(g=%7^xFnR(;(WT8T9)2m3(w z9~+7h`hs_K{I+(|Wu*P5_jVmHj5{-l zsoRCnkdqq$RGZQpK7eL;IZ4!e$Mr8TSU_iCV7#qf&N(% zE5Y%9!zhg4pH+ALbGzs{Wr^$>s&8!qs1%#jl+jmvwMyl@tIev}S@|?U`4;M0(3bVx zQ698YPk8>3?-f0etFd8Z72pMVBr6~s&TG8HL)7?@8+Lb`7}@9=`&{>lPQSMcd0t*k zMzjh`YxP*e{|3hijm$D~;i}lrRWwU*t#zZ(#@qgLhGsr#zP29Ot^MS;jw}9> zI4<$vXz#c5-uEYBuU8C2|JplSHgNWbfBuH29Uba_D)l&5n@ zr+i8$KS@#|Ivk@VUIEk7)9SSz!H>H7eX2Zi?4)x7VurG&#tA{`5-lZ(D+#-~xLRhr zZYcZe@Cafm1sDJDt>EyfJZj9ZL^%`f(y}#3oBx1%_w&E)4#vwv6~W6#2X&>&+_U1x zBj2H)a!18+=iu7}duyAww|sIDCz4M#q*5*>7`)_gO-gjXT)pha_w0~X(Y^G~Wd!0? zI53t|tDuHEwN_iq%J6dlt-LQUecAFv%sPgFP!U^qdLT{>#h7Y#l@{Izeok=(m2HZp z=3WF^7TggbFPIGQv6!ivS;Fu+$QxkW5x=9m*A6z<@@jZ4{J+f2Ar9@6oap}~nJg1n zy4`reY6coPzF2Y2gHZ+YqqP|MFf5df-~&uV56OX;e;Jvc@T_G?Kc~9}>1aNDOUWW} z2t2v;y~?Q&O9ni0hCo8*6SF#&$k!gOTKUrrR`=%<=Ok6pMbI(;iQxj_rMuE0MCO4W z6#4t&e3|4_<9}@}-$>pum+mwpCU4r-inh|^^36xfD?$-xj2_peI4?9x&1{0d<)LTW zO?3$i7Az?el~5D$=NOXhr0{5f1RU|U#Gpidj-cG}njf1<(Q?+8shUGOWqX5$muI9& zAW*XlJy1R`yABuIn`xm#}0^+wn-5n{aRqo$ys_QURD zqR9Cxht8+{?t&;Z8zVJJnD4-k<ihEwSvTp~;@oU0#dCZ!6snKVO*rCNQ(hS&_VARdu{&DT#QJ&c%7@MJ z&A}k2vzUsolK_Y=`$(^f7m+p@F=@k;@hW|VeB0Kv*o3?vpAc4=ORna1g6_LcjbR>_ zoK^3`)29Izxzbr<2c!0y2g8l0VBvrp$^>v--!W6lk1)~_&EaxY7T7%a+QHMwv09 zi^Hc`=F+jdZ&JN)j~1U}#@s_fj6u^Y|V!8Vt^*hn=sBe_CF0>Dx3uG$4;9?0$|@ml%X$`NDHEK8e>{xWi@QAowUvT1vxpOP(`v z%z7jBCTBNN>|&ahZP}WnIqO}243*P41tuYkE!@9ajLpzcJKTl-M5`v2Np9=wdY060 zsn}pN57DZQ-oo8%#HS~24@v6KmnH-iaJZmq+{oWEU!J&30^^U848Gs-fCICZF}3^r zxeOQ?`K21`N`pq(sYsGW3D^wYkWq9P76{SZYpUTJ2aUvv{VKJ!0_h__4FU88N)(0T z0bO^+$Ac+B-R9H8pf;iGE#ZPIz+MHy_H3E2%xtpPB;HwWxY;<4Y(?HmC{44Wk@JI zq_A?*%bdH_+bdj}>7$S~geQBab@Df1p%wX2tmV}&LVj+fj$9=#A+Hay1^()a2*nWv zS(X364-6o`Qmg^k*lmNO?{}tl;y+x-3sQ=E6@sjm1qHHqFIpC+tOogD2sSE5)8cbw z@GrQ{rPQCh?%)9N39txg&&xFjj0(=$mUa{(rC@GE8 zZA^@ldk(Ud1+27pO!tvZvi(0vMf+5h(@Q%&iWkMI`>IUH?vLsN;=hjI!@Mg^!&u#? zh)$L@$#oz+!gb#!Sr7D^VNzKoZ@}g4lpr^k8Gk@;xp8Ii2WIw^h0>H>$V#;Ft-^S1 z7B8s>xhi+~QiOP&5$6gTYiAxGb>GVz(uCYEhI2|@tc&jFFc(kCcYQTCoZgS7g_~;} zJVt{*nR^~cy%RnsiGe9edtJRgvT;&AX4Y*b!tkKiZ7T7aR|YZM`|`a#mjkoTff`o2 zP9ML-y{ir}Tvby4+Xy}4kT=n-bE4V%+8cN5Ue9zqHD0>?$VI86k2ER}Wrt5UQqDyHuH@{m{4wYv{_dl+jmiW>WV!KWBc2+#|GH`ls+fxF`4GV6 zf!qp9N+(vJ6MsJSAiseqk5{m4?83;bK>6Vy*X`TfTs_>Gq{20d!e5}$T)ihe*cyrZ zgSSS*nYi!BdA>w#)#JKC&p%?YYY-}^0S|2Ja{6xIxZmD9`paKT@HHP0QIWlRGWRCS z<1D#JbH%!ZYiG1m^lImOg&l*J*S@WZVpfBkkT4)pXS9@?P4^W`E2@lG;UmiI~eHsW&2%ursGHaX0 zrzS)EBDu=h-m}jAx*btvPkGg88!stsJ0p4Fl>mOEg8hXL#7T`KJ*19YQ&@)Fx)fVO z0762v_PV`+=fty)yZgGm1@0~64d>e#ydqMVC+wwB9+54l-!87li&GH|<0%alQCV0w z?!(rv8q7lg6Em|^@Nm)Dyp;I2BJvL}*;Cv!+VsS6JH|~};{VNNlk#@2aD@EQ_0i{` z%cl(_+H8-T@-2<`A>i-xyZ-u3g?V2xFC5TXcwm6LlzV$SuR({nRl|(bYG_JUoIj->6HxoHlm309*u>FHZL^?RVkJG*q@zMADolacK0#fNK}?F?NYojVsb>+NF^8F&`W@55vp z7N~uh{Rau`_RC6*SI`grK$mY(S*^lTYpThUfmc}By=4?0+-EecZm*oTvm$u7Gi5XwKSf{r(V1;E9cTB0D zcRk?iFKyfYlp(|`*2M0PQgdTeob{5NVUrw%^YgSQF{ zkjH|Y9%LH`YlHY5twgIb!!)YT03~W~7Dd65><0-Cg-LP57IqXjAETxG7V*h|p22_8 z;6C3^V{dyLH7-jUMBH+UPIOm|2nEc5$cIW2!^;YZ6HBPO@&or4q8l|i_j{-~tMNT(Lt^V&1hm???wN zY0eOy${m_}su%}T+@U1QJ<@i?>%7Owi^ZNX7T1KLEi=VHZ9jDr6_HO~$PXQxAIH6K z!{}JG6k)O~kkV*@qC{|n%B2Favo!4mUIrq8aVc1Ihm2#CTD}(5>j$S^s!*&E`cbrD zCG$tpMyv&5sFv~un-yVY7aIs2oC5GZOVtMYg9?k3Xr`hqm_3trI2>Cnh zyesdE5e~g09xxW(HSP5`FFEB@kE^=mZRT)A*bp3;K z!zZb0S`4sMk2cKH^f{MY*^nHmTq1qPB|P{qjz@}<8v$J!T%X~`JAc#7d+b1IQYg@F zU%5&`(44z+Gq<8@^~c8XZQJYc20dC-GP6f+Fb~Zu>NzP?XkV)Od{))S@AoY}SIdTO zXO@-ij`zhn_)V^Qqd<0@V@3}pvm?tks@>Vmmc4fn$Deo(@VYpVC!)^4({huXXA}$M zU30;f?4%WuC&|NUocDX$r$0`XRjvB4MGtJe*8UYDceq;hJmK-`mcXR|anCpnNHDt^ zRr(*2YW26n7A9vqeJfVNLh|rzA7)X!rh(o?#Zt+x3X)`f;vRN`HGeUP+<8j|N{_0* z>sI3pg*W~qW{b!Zrg<h=@ex*lQu z`-JkmV~*9v_A3K5PFMbs(nq^0PZLL6F`rjkSsG!z_DdhPwQ(V1ko<&ZB}f zdvf3l(AHk+3)u1$W`_o+Sh%)| ztepVTt^YRV0|mi{i)8i6fjsRYOX(c?^d$yA9@~Nagmhl{%9{DqEnD|p_9W`*JW+wO z&@4rU2~WrUm3S{r<;bEf0%3HH&u()bWcOGF5yf@Hj+x7PsWP`Y0*UJBs;}7}#hZLO zDb|D9SSj>H{4?vLbO?4-uS{8ZC5$LPsN;K)8Y}SHaLqQ%Scf}x7M&tx>(pVgSR&Rc z6bjfBNu6cPr5yeYVEF<7ybd3<5yT6NJy=wdM0T=(^YebPFo4&c!r;#IT5+-yj+xsn zzqiIHsi)Yls~)tTLw%pPxO*LmCp8e7#qS⁣ob|d;ouI2DQfN@O`3MdAZD{n1n#M z9^1LMCGBNDl{N7!^`zn%cK+Dk;v`N^hs2EA1-$KorSd{%IsQ6lBLXjMbV^7B#x#CS zBmmQ=1UEr=i9Vk@Oo^pTOKum$D(ux)BUCcZ4AwT-5=g7vYJHbL03g@IF+-!;MhIV0E&X^$5D%I*#cvE7$^7p75eb8 zBa?n1`}eAhf>2lpAQ-$>x{jPt_C*~b(4%J723>&wTmvc(6-={AV+fnTj{tiyZZKpi_D-wA^o>sE=fiiQECj^+64FX_#c5}W{0azEaHzkC zOlW9F-;H`Upld=Rf!k=;GJ`$?$8qhA)F2F+iQh)e-e8DDAN&{6N@m25epn6KSe2Zj zrYrV!P1ZrZaxjN-k_ug_SZFXHp0&TOxnH7xRqSBD;O57#Mq4#DnH|gG`ggUyO;}X# zag$#|l-zi?RksCVtu?zGXhLmAQNRp&g4jTBXNxun*fI_S+%dlLuH!i{j?}eV!H0;l zAW?4^3CW*We5_Cab`O#*?(FAC)kQ=a5pZYdIW8`$+_7OsX1$zCwX`6I2d!}yVo
N?hj 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 literal 32857 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 T5bzX%g*oJ)%F>Tq6|ay(Fq|@ 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 YN4zmRj*shI{mVEg%Bsm!NtuNHFUpX@*Z=?k literal 0 HcmV?d00001 diff --git a/docs/images/autotype_selection_dialog_type_menu.png b/docs/images/autotype_selection_dialog_type_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..f0e83ee80fd985e4e49976a797b78ad01be02489 GIT binary patch literal 36766 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 literal 0 HcmV?d00001 diff --git a/docs/topics/AutoType.adoc b/docs/topics/AutoType.adoc index 6e608a9ab..eb6a76909 100644 --- a/docs/topics/AutoType.adoc +++ b/docs/topics/AutoType.adoc @@ -69,12 +69,22 @@ image::autotype_entry_sequences.png[] === Performing Global Auto-Type The global Auto-Type keyboard shortcut is used when you have focus on the window you want to type into. To make use of this feature, you must have previously configured an Auto-Type hotkey. -Pressing the global Auto-Type hotkey cause KeePassXC to search the database for entries that match the window title. Multiple matches may be returned and will cause the sequence selection dialog to appear. Click on a sequence line will immediately execute the Auto-Type action. A search box is also available in case numerous matches are returned. +When you press the global Auto-Type hotkey, KeePassXC searches all unlocked databases for entries that match the focused window title. The Auto-Type selection dialog will appear in the following circumstances: there are no matches found, there are multiple matches found, or the setting "Always ask before performing Auto-Type" is enabled. .Auto-Type sequence selection image::autotype_selection_dialog.png[,70%] -TIP: The _Sequence_ column will only appear when there are different sequences defined by one or more entries displayed in the selection dialog. +Perform the selected Auto-Type sequence by double clicking the desired row or pressing _Enter_. Press the up and down arrows to navigate the list. Sequences can be filtered through the text edit field. + +.Auto-Type search database +image::autotype_selection_dialog_search.png[,70%] + +Search the unlocked databases by activating Search Database radio button. Use the text edit field to issue search queries using the same syntax as database searching. + +.Additional Auto-Type choices +image::autotype_selection_dialog_type_menu.png[,70%] + +The option to type just the username, password, or current TOTP value is available by right clicking the desired row or expanding the Type Sequence button options. === Performing Entry-Level Auto-Type You can quickly activate the default Auto-Type sequence for a particular entry using Entry-Level Auto-Type. For this operation, the KeePassXC window will be minimized and the Auto-Type sequence occurs in the previously selected window. You can perform Entry-Level Auto-Type from the toolbar icon *(A)*, entry context menu *(B)*, or by pressing `Ctrl+Shift+V`. diff --git a/docs/topics/DownloadInstall.adoc b/docs/topics/DownloadInstall.adoc index cc6f1fcb7..5f692638c 100644 --- a/docs/topics/DownloadInstall.adoc +++ b/docs/topics/DownloadInstall.adoc @@ -60,3 +60,5 @@ To install the KeePassXC app on macOS, double click on the downloaded DMG file a image::macos_install.png[,80%] // end::content[] +// tag::advanced[] +// end::advanced[] \ No newline at end of file diff --git a/docs/topics/Welcome.adoc b/docs/topics/Welcome.adoc index 507419029..4eb168e6c 100644 --- a/docs/topics/Welcome.adoc +++ b/docs/topics/Welcome.adoc @@ -44,3 +44,5 @@ KeePassXC has numerous features for novice and power users alike. This guide wil ** FreeDesktop.org Secret Service (replace Gnome keyring, etc.) ** Additional encryption choices: Twofish and ChaCha20 // end::content[] +// tag::advanced[] +// end::advanced[] \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b308df2f3..ba8acdc77 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,7 +33,6 @@ endif(NOT ZXCVBN_LIBRARIES) set(keepassx_SOURCES core/Alloc.cpp core/AutoTypeAssociations.cpp - core/AutoTypeMatch.cpp core/Base32.cpp core/Bootstrap.cpp core/Clock.cpp @@ -139,8 +138,6 @@ set(keepassx_SOURCES gui/csvImport/CsvImportWizard.cpp gui/csvImport/CsvParserModel.cpp gui/entry/AutoTypeAssociationsModel.cpp - gui/entry/AutoTypeMatchModel.cpp - gui/entry/AutoTypeMatchView.cpp gui/entry/EditEntryWidget.cpp gui/entry/EntryAttachmentsModel.cpp gui/entry/EntryAttachmentsWidget.cpp @@ -273,11 +270,10 @@ set(autotype_SOURCES core/Tools.cpp autotype/AutoType.cpp autotype/AutoTypeAction.cpp - autotype/AutoTypeFilterLineEdit.cpp + autotype/AutoTypeMatchModel.cpp + autotype/AutoTypeMatchView.cpp autotype/AutoTypeSelectDialog.cpp - autotype/AutoTypeSelectView.cpp autotype/ShortcutWidget.cpp - autotype/WildcardMatcher.cpp autotype/WindowSelectComboBox.cpp) if(MINGW) diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index 94dd432c9..7a52491b3 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -21,13 +21,12 @@ #include #include #include +#include #include "config-keepassx.h" #include "autotype/AutoTypePlatformPlugin.h" #include "autotype/AutoTypeSelectDialog.h" -#include "autotype/WildcardMatcher.h" -#include "core/AutoTypeMatch.h" #include "core/Config.h" #include "core/Database.h" #include "core/Entry.h" @@ -250,12 +249,10 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow) return; } - QList sequences = autoTypeSequences(entry); - if (sequences.isEmpty()) { - return; + auto sequences = entry->autoTypeSequences(); + if (!sequences.isEmpty()) { + executeAutoTypeActions(entry, hideWindow, sequences.first()); } - - executeAutoTypeActions(entry, hideWindow, sequences.first()); } /** @@ -273,6 +270,11 @@ void AutoType::performAutoTypeWithSequence(const Entry* entry, const QString& se void AutoType::startGlobalAutoType() { + // Never Auto-Type into KeePassXC itself + if (qApp->focusWindow()) { + return; + } + m_windowForGlobal = m_plugin->activeWindow(); m_windowTitleForGlobal = m_plugin->activeWindowTitle(); #ifdef Q_OS_MACOS @@ -331,58 +333,62 @@ void AutoType::performGlobalAutoType(const QList>& dbLi for (const auto& db : dbList) { const QList dbEntries = db->rootGroup()->entriesRecursive(); - for (Entry* entry : dbEntries) { + for (auto entry : dbEntries) { + auto group = entry->group(); + if (!group || !group->resolveAutoTypeEnabled() || !entry->autoTypeEnabled()) { + continue; + } + if (hideExpired && entry->isExpired()) { continue; } - const QSet sequences = autoTypeSequences(entry, m_windowTitleForGlobal).toSet(); - for (const QString& sequence : sequences) { - if (!sequence.isEmpty()) { - matchList << AutoTypeMatch(entry, sequence); - } + auto sequences = entry->autoTypeSequences(m_windowTitleForGlobal).toSet(); + for (const auto& sequence : sequences) { + matchList << AutoTypeMatch(entry, sequence); } } } - if (matchList.isEmpty()) { - if (qobject_cast(QCoreApplication::instance())) { - auto* msgBox = new QMessageBox(); - msgBox->setAttribute(Qt::WA_DeleteOnClose); - msgBox->setWindowTitle(tr("Auto-Type - KeePassXC")); - msgBox->setText(tr("Couldn't find an entry that matches the window title:") - .append("\n\n") - .append(m_windowTitleForGlobal)); - msgBox->setIcon(QMessageBox::Information); - msgBox->setStandardButtons(QMessageBox::Ok); -#ifdef Q_OS_MACOS - m_plugin->raiseOwnWindow(); - Tools::wait(200); -#endif - msgBox->exec(); - restoreWindowState(); + // Show the selection dialog if we always ask, have multiple matches, or no matches + if (config()->get(Config::Security_AutoTypeAsk).toBool() || matchList.size() > 1 || matchList.isEmpty()) { + // Close any open modal windows that would interfere with the process + if (qApp->modalWindow()) { + qApp->modalWindow()->close(); } - m_inGlobalAutoTypeDialog.unlock(); - emit autotypeRejected(); - } else if ((matchList.size() == 1) && !config()->get(Config::Security_AutoTypeAsk).toBool()) { - executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence, m_windowForGlobal); - m_inGlobalAutoTypeDialog.unlock(); - } else { auto* selectDialog = new AutoTypeSelectDialog(); + selectDialog->setMatches(matchList, dbList); - // connect slots, both of which must unlock the m_inGlobalAutoTypeDialog mutex - connect(selectDialog, SIGNAL(matchActivated(AutoTypeMatch)), SLOT(performAutoTypeFromGlobal(AutoTypeMatch))); - connect(selectDialog, SIGNAL(rejected()), SLOT(autoTypeRejectedFromGlobal())); + connect(getMainWindow(), &MainWindow::databaseLocked, selectDialog, &AutoTypeSelectDialog::reject); + connect(selectDialog, &AutoTypeSelectDialog::matchActivated, this, [this](AutoTypeMatch match) { + restoreWindowState(); + QApplication::processEvents(); + m_plugin->raiseWindow(m_windowForGlobal); + executeAutoTypeActions(match.first, nullptr, match.second, m_windowForGlobal); + resetAutoTypeState(); + }); + connect(selectDialog, &QDialog::rejected, this, [this] { + restoreWindowState(); + resetAutoTypeState(); + emit autotypeRejected(); + }); - selectDialog->setMatchList(matchList); #ifdef Q_OS_MACOS m_plugin->raiseOwnWindow(); Tools::wait(200); #endif selectDialog->show(); selectDialog->raise(); - // necessary when the main window is minimized selectDialog->activateWindow(); + } else if (!matchList.isEmpty()) { + // Only one match and not asking, do it! + executeAutoTypeActions(matchList.first().first, nullptr, matchList.first().second, m_windowForGlobal); + resetAutoTypeState(); + } else { + // We should never get here + Q_ASSERT(false); + resetAutoTypeState(); + emit autotypeRejected(); } } @@ -399,29 +405,12 @@ void AutoType::restoreWindowState() #endif } -void AutoType::performAutoTypeFromGlobal(AutoTypeMatch match) +void AutoType::resetAutoTypeState() { - restoreWindowState(); - - m_plugin->raiseWindow(m_windowForGlobal); - executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowForGlobal); - - // make sure the mutex is definitely locked before we unlock it - Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock()); - m_inGlobalAutoTypeDialog.unlock(); -} - -void AutoType::autoTypeRejectedFromGlobal() -{ - // this slot can be called twice when the selection dialog is deleted, - // so make sure the mutex is locked before we try unlocking it - Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock()); - m_inGlobalAutoTypeDialog.unlock(); m_windowForGlobal = 0; m_windowTitleForGlobal.clear(); - - restoreWindowState(); - emit autotypeRejected(); + Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock()); + m_inGlobalAutoTypeDialog.unlock(); } /** @@ -622,101 +611,6 @@ QList AutoType::createActionFromTemplate(const QString& tmpl, c return list; } -/** - * Retrive the autotype sequences matches for a given windowTitle - * This returns a list with priority ordering. If you don't want duplicates call .toSet() on it. - */ -QList AutoType::autoTypeSequences(const Entry* entry, const QString& windowTitle) -{ - QList sequenceList; - const Group* group = entry->group(); - - if (!group || !entry->autoTypeEnabled()) { - return sequenceList; - } - - do { - if (group->autoTypeEnabled() == Group::Disable) { - return sequenceList; - } else if (group->autoTypeEnabled() == Group::Enable) { - break; - } - group = group->parentGroup(); - - } while (group); - - if (!windowTitle.isEmpty()) { - const QList assocList = entry->autoTypeAssociations()->getAll(); - for (const AutoTypeAssociations::Association& assoc : assocList) { - const QString window = entry->resolveMultiplePlaceholders(assoc.window); - if (windowMatches(windowTitle, window)) { - if (!assoc.sequence.isEmpty()) { - sequenceList.append(assoc.sequence); - } else { - sequenceList.append(entry->effectiveAutoTypeSequence()); - } - } - } - - if (config()->get(Config::AutoTypeEntryTitleMatch).toBool() - && windowMatchesTitle(windowTitle, entry->resolvePlaceholder(entry->title()))) { - sequenceList.append(entry->effectiveAutoTypeSequence()); - } - - if (config()->get(Config::AutoTypeEntryURLMatch).toBool() - && windowMatchesUrl(windowTitle, entry->resolvePlaceholder(entry->url()))) { - sequenceList.append(entry->effectiveAutoTypeSequence()); - } - - if (sequenceList.isEmpty()) { - return sequenceList; - } - } else { - sequenceList.append(entry->effectiveAutoTypeSequence()); - } - - return sequenceList; -} - -/** - * Checks if a window title matches a pattern - */ -bool AutoType::windowMatches(const QString& windowTitle, const QString& windowPattern) -{ - if (windowPattern.startsWith("//") && windowPattern.endsWith("//") && windowPattern.size() >= 4) { - QRegExp regExp(windowPattern.mid(2, windowPattern.size() - 4), Qt::CaseInsensitive, QRegExp::RegExp2); - return (regExp.indexIn(windowTitle) != -1); - } - return WildcardMatcher(windowTitle).match(windowPattern); -} - -/** - * Checks if a window title matches an entry Title - * The entry title should be Spr-compiled by the caller - */ -bool AutoType::windowMatchesTitle(const QString& windowTitle, const QString& resolvedTitle) -{ - return !resolvedTitle.isEmpty() && windowTitle.contains(resolvedTitle, Qt::CaseInsensitive); -} - -/** - * Checks if a window title matches an entry URL - * The entry URL should be Spr-compiled by the caller - */ -bool AutoType::windowMatchesUrl(const QString& windowTitle, const QString& resolvedUrl) -{ - if (!resolvedUrl.isEmpty() && windowTitle.contains(resolvedUrl, Qt::CaseInsensitive)) { - return true; - } - - QUrl url(resolvedUrl); - if (url.isValid() && !url.host().isEmpty()) { - return windowTitle.contains(url.host(), Qt::CaseInsensitive); - } - - return false; -} - /** * Checks if the overall syntax of an autotype sequence is fine */ diff --git a/src/autotype/AutoType.h b/src/autotype/AutoType.h index 8d0cf3d14..0af78c4c5 100644 --- a/src/autotype/AutoType.h +++ b/src/autotype/AutoType.h @@ -24,7 +24,7 @@ #include #include -#include "core/AutoTypeMatch.h" +#include "autotype/AutoTypeMatch.h" class AutoTypeAction; class AutoTypeExecutor; @@ -68,8 +68,6 @@ signals: private slots: void startGlobalAutoType(); - void performAutoTypeFromGlobal(AutoTypeMatch match); - void autoTypeRejectedFromGlobal(); void unloadPlugin(); private: @@ -89,11 +87,8 @@ private: WId window = 0); bool parseActions(const QString& sequence, const Entry* entry, QList& actions); QList createActionFromTemplate(const QString& tmpl, const Entry* entry); - QList autoTypeSequences(const Entry* entry, const QString& windowTitle = QString()); - bool windowMatchesTitle(const QString& windowTitle, const QString& resolvedTitle); - bool windowMatchesUrl(const QString& windowTitle, const QString& resolvedUrl); - bool windowMatches(const QString& windowTitle, const QString& windowPattern); void restoreWindowState(); + void resetAutoTypeState(); QMutex m_inAutoType; QMutex m_inGlobalAutoTypeDialog; diff --git a/src/autotype/AutoTypeFilterLineEdit.cpp b/src/autotype/AutoTypeFilterLineEdit.cpp deleted file mode 100644 index d94292d26..000000000 --- a/src/autotype/AutoTypeFilterLineEdit.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 KeePassXC Team - * - * 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 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "AutoTypeFilterLineEdit.h" -#include - -void AutoTypeFilterLineEdit::keyPressEvent(QKeyEvent* event) -{ - if (event->key() == Qt::Key_Up) { - emit keyUpPressed(); - } else if (event->key() == Qt::Key_Down) { - emit keyDownPressed(); - } else { - QLineEdit::keyPressEvent(event); - } -} - -void AutoTypeFilterLineEdit::keyReleaseEvent(QKeyEvent* event) -{ - if (event->key() == Qt::Key_Escape) { - emit escapeReleased(); - } else { - QLineEdit::keyReleaseEvent(event); - } -} diff --git a/src/autotype/AutoTypeFilterLineEdit.h b/src/autotype/AutoTypeFilterLineEdit.h deleted file mode 100644 index 6f8ca570d..000000000 --- a/src/autotype/AutoTypeFilterLineEdit.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2019 KeePassXC Team - * - * 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 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSX_AUTOTYPEFILTERLINEEDIT_H -#define KEEPASSX_AUTOTYPEFILTERLINEEDIT_H - -#include - -class AutoTypeFilterLineEdit : public QLineEdit -{ - Q_OBJECT - -public: - AutoTypeFilterLineEdit(QWidget* widget) - : QLineEdit(widget) - { - } - -protected: - virtual void keyPressEvent(QKeyEvent* event); - virtual void keyReleaseEvent(QKeyEvent* event); -signals: - void keyUpPressed(); - void keyDownPressed(); - void escapeReleased(); -}; - -#endif // KEEPASSX_AUTOTYPEFILTERLINEEDIT_H diff --git a/src/core/AutoTypeMatch.h b/src/autotype/AutoTypeMatch.h similarity index 56% rename from src/core/AutoTypeMatch.h rename to src/autotype/AutoTypeMatch.h index 768cf1682..4f396546a 100644 --- a/src/core/AutoTypeMatch.h +++ b/src/autotype/AutoTypeMatch.h @@ -1,6 +1,5 @@ /* - * Copyright (C) 2015 David Wu - * Copyright (C) 2017 KeePassXC Team + * Copyright (C) 2021 KeePassXC Team * * 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 @@ -16,26 +15,14 @@ * along with this program. If not, see . */ -#ifndef KEEPASSX_AUTOTYPEMATCH_H -#define KEEPASSX_AUTOTYPEMATCH_H +#ifndef KPXC_AUTOTYPEMATCH_H +#define KPXC_AUTOTYPEMATCH_H -#include +#include +#include #include class Entry; +typedef QPair, QString> AutoTypeMatch; -struct AutoTypeMatch -{ - Entry* entry; - QString sequence; - - AutoTypeMatch(); - AutoTypeMatch(Entry* entry, QString sequence); - - bool operator==(const AutoTypeMatch& other) const; - bool operator!=(const AutoTypeMatch& other) const; -}; - -Q_DECLARE_TYPEINFO(AutoTypeMatch, Q_MOVABLE_TYPE); - -#endif // KEEPASSX_AUTOTYPEMATCH_H +#endif // KPXC_AUTOTYPEMATCH_H diff --git a/src/gui/entry/AutoTypeMatchModel.cpp b/src/autotype/AutoTypeMatchModel.cpp similarity index 88% rename from src/gui/entry/AutoTypeMatchModel.cpp rename to src/autotype/AutoTypeMatchModel.cpp index ac2d0f874..f15df233b 100644 --- a/src/gui/entry/AutoTypeMatchModel.cpp +++ b/src/autotype/AutoTypeMatchModel.cpp @@ -56,7 +56,7 @@ void AutoTypeMatchModel::setMatchList(const QList& matches) QSet databases; for (AutoTypeMatch& match : m_matches) { - databases.insert(match.entry->group()->database()); + databases.insert(match.first->group()->database()); } for (Database* db : asConst(databases)) { @@ -88,7 +88,6 @@ int AutoTypeMatchModel::rowCount(const QModelIndex& parent) const int AutoTypeMatchModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); - return 4; } @@ -103,30 +102,30 @@ QVariant AutoTypeMatchModel::data(const QModelIndex& index, int role) const if (role == Qt::DisplayRole) { switch (index.column()) { case ParentGroup: - if (match.entry->group()) { - return match.entry->group()->name(); + if (match.first->group()) { + return match.first->group()->name(); } break; case Title: - return match.entry->resolveMultiplePlaceholders(match.entry->title()); + return match.first->resolveMultiplePlaceholders(match.first->title()); case Username: - return match.entry->resolveMultiplePlaceholders(match.entry->username()); + return match.first->resolveMultiplePlaceholders(match.first->username()); case Sequence: - return match.sequence; + return match.second; } } else if (role == Qt::DecorationRole) { switch (index.column()) { case ParentGroup: - if (match.entry->group()) { - return match.entry->group()->iconPixmap(); + if (match.first->group()) { + return match.first->group()->iconPixmap(); } break; case Title: - return match.entry->iconPixmap(); + return match.first->iconPixmap(); } } else if (role == Qt::FontRole) { QFont font; - if (match.entry->isExpired()) { + if (match.first->isExpired()) { font.setStrikeOut(true); } return font; @@ -157,7 +156,7 @@ void AutoTypeMatchModel::entryDataChanged(Entry* entry) { for (int row = 0; row < m_matches.size(); ++row) { AutoTypeMatch match = m_matches[row]; - if (match.entry == entry) { + if (match.first == entry) { emit dataChanged(index(row, 0), index(row, columnCount() - 1)); } } @@ -167,7 +166,7 @@ void AutoTypeMatchModel::entryAboutToRemove(Entry* entry) { for (int row = 0; row < m_matches.size(); ++row) { AutoTypeMatch match = m_matches[row]; - if (match.entry == entry) { + if (match.first == entry) { beginRemoveRows(QModelIndex(), row, row); m_matches.removeAt(row); endRemoveRows(); diff --git a/src/gui/entry/AutoTypeMatchModel.h b/src/autotype/AutoTypeMatchModel.h similarity index 98% rename from src/gui/entry/AutoTypeMatchModel.h rename to src/autotype/AutoTypeMatchModel.h index 58b89465b..e5c86e84d 100644 --- a/src/gui/entry/AutoTypeMatchModel.h +++ b/src/autotype/AutoTypeMatchModel.h @@ -21,7 +21,7 @@ #include -#include "core/AutoTypeMatch.h" +#include "autotype/AutoTypeMatch.h" class Entry; class Group; diff --git a/src/autotype/AutoTypeMatchView.cpp b/src/autotype/AutoTypeMatchView.cpp new file mode 100644 index 000000000..453d9072e --- /dev/null +++ b/src/autotype/AutoTypeMatchView.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015 David Wu + * Copyright (C) 2017 KeePassXC Team + * + * 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 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "AutoTypeMatchView.h" + +#include "core/Entry.h" +#include "gui/Clipboard.h" +#include "gui/Icons.h" + +#include +#include +#include +#include + +class CustomSortFilterProxyModel : public QSortFilterProxyModel +{ +public: + explicit CustomSortFilterProxyModel(QObject* parent = nullptr) + : QSortFilterProxyModel(parent){}; + ~CustomSortFilterProxyModel() override = default; + + // Only search the first three columns (ie, ignore sequence column) + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override + { + auto index0 = sourceModel()->index(sourceRow, 0, sourceParent); + auto index1 = sourceModel()->index(sourceRow, 1, sourceParent); + auto index2 = sourceModel()->index(sourceRow, 2, sourceParent); + + return sourceModel()->data(index0).toString().contains(filterRegExp()) + || sourceModel()->data(index1).toString().contains(filterRegExp()) + || sourceModel()->data(index2).toString().contains(filterRegExp()); + } +}; + +AutoTypeMatchView::AutoTypeMatchView(QWidget* parent) + : QTableView(parent) + , m_model(new AutoTypeMatchModel(this)) + , m_sortModel(new CustomSortFilterProxyModel(this)) +{ + m_sortModel->setSourceModel(m_model); + m_sortModel->setDynamicSortFilter(true); + m_sortModel->setSortLocaleAware(true); + m_sortModel->setSortCaseSensitivity(Qt::CaseInsensitive); + m_sortModel->setFilterKeyColumn(-1); + m_sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + setModel(m_sortModel); + + sortByColumn(0, Qt::AscendingOrder); + + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, &QTableView::doubleClicked, this, [this](const QModelIndex& index) { + emit matchActivated(matchFromIndex(index)); + }); +} + +void AutoTypeMatchView::keyPressEvent(QKeyEvent* event) +{ + if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) { + emit matchActivated(matchFromIndex(currentIndex())); + } + + QTableView::keyPressEvent(event); +} + +void AutoTypeMatchView::setMatchList(const QList& matches) +{ + m_model->setMatchList(matches); + m_sortModel->setFilterWildcard({}); + + horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); + selectionModel()->setCurrentIndex(m_sortModel->index(0, 0), + QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + emit currentMatchChanged(currentMatch()); +} + +void AutoTypeMatchView::filterList(const QString& filter) +{ + m_sortModel->setFilterWildcard(filter); + setCurrentIndex(m_sortModel->index(0, 0)); +} + +AutoTypeMatch AutoTypeMatchView::currentMatch() +{ + QModelIndexList list = selectionModel()->selectedRows(); + if (list.size() == 1) { + return m_model->matchFromIndex(m_sortModel->mapToSource(list.first())); + } + return {}; +} + +AutoTypeMatch AutoTypeMatchView::matchFromIndex(const QModelIndex& index) +{ + if (index.isValid()) { + return m_model->matchFromIndex(m_sortModel->mapToSource(index)); + } + return {}; +} + +void AutoTypeMatchView::currentChanged(const QModelIndex& current, const QModelIndex& previous) +{ + auto match = matchFromIndex(current); + emit currentMatchChanged(match); + QTableView::currentChanged(current, previous); +} diff --git a/src/gui/entry/AutoTypeMatchView.h b/src/autotype/AutoTypeMatchView.h similarity index 72% rename from src/gui/entry/AutoTypeMatchView.h rename to src/autotype/AutoTypeMatchView.h index 69d0795d9..90442baa6 100644 --- a/src/gui/entry/AutoTypeMatchView.h +++ b/src/autotype/AutoTypeMatchView.h @@ -19,42 +19,37 @@ #ifndef KEEPASSX_AUTOTYPEMATCHVIEW_H #define KEEPASSX_AUTOTYPEMATCHVIEW_H -#include +#include -#include "core/AutoTypeMatch.h" +#include "autotype/AutoTypeMatch.h" +#include "autotype/AutoTypeMatchModel.h" -#include "gui/entry/AutoTypeMatchModel.h" +class QSortFilterProxyModel; -class SortFilterHideProxyModel; - -class AutoTypeMatchView : public QTreeView +class AutoTypeMatchView : public QTableView { Q_OBJECT public: explicit AutoTypeMatchView(QWidget* parent = nullptr); AutoTypeMatch currentMatch(); - void setCurrentMatch(const AutoTypeMatch& match); AutoTypeMatch matchFromIndex(const QModelIndex& index); void setMatchList(const QList& matches); - void setFirstMatchActive(); + void filterList(const QString& filter); signals: + void currentMatchChanged(AutoTypeMatch match); void matchActivated(AutoTypeMatch match); - void matchSelectionChanged(); - void matchTextCopied(); protected: void keyPressEvent(QKeyEvent* event) override; -private slots: - void emitMatchActivated(const QModelIndex& index); - void userNameCopied(); - void passwordCopied(); +protected slots: + void currentChanged(const QModelIndex& current, const QModelIndex& previous) override; private: AutoTypeMatchModel* const m_model; - SortFilterHideProxyModel* const m_sortModel; + QSortFilterProxyModel* const m_sortModel; }; #endif // KEEPASSX_AUTOTYPEMATCHVIEW_H diff --git a/src/autotype/AutoTypeSelectDialog.cpp b/src/autotype/AutoTypeSelectDialog.cpp index e91355f4a..836ba3070 100644 --- a/src/autotype/AutoTypeSelectDialog.cpp +++ b/src/autotype/AutoTypeSelectDialog.cpp @@ -1,6 +1,6 @@ /* + * Copyright (C) 2021 KeePassXC Team * Copyright (C) 2012 Felix Geyer - * Copyright (C) 2020 KeePassXC Team * * 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 @@ -17,168 +17,324 @@ */ #include "AutoTypeSelectDialog.h" +#include "ui_AutoTypeSelectDialog.h" -#include +#include +#include +#include #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) #include #else #include #endif -#include -#include -#include -#include -#include -#include -#include "autotype/AutoTypeSelectView.h" -#include "core/AutoTypeMatch.h" #include "core/Config.h" +#include "core/Database.h" +#include "core/Entry.h" +#include "core/EntrySearcher.h" +#include "gui/Clipboard.h" #include "gui/Icons.h" -#include "gui/entry/AutoTypeMatchModel.h" AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent) : QDialog(parent) - , m_view(new AutoTypeSelectView(this)) - , m_filterLineEdit(new AutoTypeFilterLineEdit(this)) - , m_matchActivatedEmitted(false) - , m_rejected(false) + , m_ui(new Ui::AutoTypeSelectDialog()) { setAttribute(Qt::WA_DeleteOnClose); // Places the window on the active (virtual) desktop instead of where the main window is. setAttribute(Qt::WA_X11BypassTransientForHint); - setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); - setWindowTitle(tr("Auto-Type - KeePassXC")); + setWindowFlags((windowFlags() | Qt::WindowStaysOnTopHint) & ~Qt::WindowContextHelpButtonHint); setWindowIcon(icons()->applicationIcon()); + buildActionMenu(); + + m_ui->setupUi(this); + + connect(m_ui->view, &AutoTypeMatchView::matchActivated, this, &AutoTypeSelectDialog::submitAutoTypeMatch); + connect(m_ui->view, &AutoTypeMatchView::currentMatchChanged, this, &AutoTypeSelectDialog::updateActionMenu); + connect(m_ui->view, &QWidget::customContextMenuRequested, this, [this](const QPoint& pos) { + if (m_ui->view->currentMatch().first) { + m_actionMenu->popup(m_ui->view->viewport()->mapToGlobal(pos)); + } + }); + + + m_ui->search->setFocus(); + m_ui->search->installEventFilter(this); + + m_searchTimer.setInterval(300); + m_searchTimer.setSingleShot(true); + + connect(m_ui->search, SIGNAL(textChanged(QString)), &m_searchTimer, SLOT(start())); + connect(m_ui->search, SIGNAL(returnPressed()), SLOT(activateCurrentMatch())); + connect(&m_searchTimer, SIGNAL(timeout()), SLOT(performSearch())); + + connect(m_ui->filterRadio, &QRadioButton::toggled, this, [this](bool checked) { + if (checked) { + // Reset to original match list + m_ui->view->setMatchList(m_matches); + performSearch(); + m_ui->search->setFocus(); + } + }); + connect(m_ui->searchRadio, &QRadioButton::toggled, this, [this](bool checked) { + if (checked) { + performSearch(); + m_ui->search->setFocus(); + } + }); + + m_actionMenu->installEventFilter(this); + m_ui->action->setMenu(m_actionMenu); + m_ui->action->installEventFilter(this); + connect(m_ui->action, &QToolButton::clicked, this, &AutoTypeSelectDialog::activateCurrentMatch); + + connect(m_ui->cancelButton, SIGNAL(clicked()), SLOT(reject())); +} + +// Required for QScopedPointer +AutoTypeSelectDialog::~AutoTypeSelectDialog() +{ +} + +void AutoTypeSelectDialog::setMatches(const QList& matches, const QList>& dbs) +{ + m_matches = matches; + m_dbs = dbs; + + m_ui->view->setMatchList(m_matches); + if (m_matches.isEmpty()) { + m_ui->searchRadio->setChecked(true); + } else { + m_ui->filterRadio->setChecked(true); + } +} + +void AutoTypeSelectDialog::submitAutoTypeMatch(AutoTypeMatch match) +{ + m_accepted = true; + accept(); + emit matchActivated(std::move(match)); +} + +void AutoTypeSelectDialog::performSearch() +{ + if (m_ui->filterRadio->isChecked()) { + m_ui->view->filterList(m_ui->search->text()); + return; + } + + auto searchText = m_ui->search->text(); + // If no search text, find all entries + if (searchText.isEmpty()) { + searchText.append("*"); + } + + EntrySearcher searcher; + QList matches; + for (const auto& db : m_dbs) { + auto found = searcher.search(searchText, db->rootGroup()); + for (auto* entry : found) { + QSet sequences; + auto defSequence = entry->effectiveAutoTypeSequence(); + if (!defSequence.isEmpty()) { + matches.append({entry, defSequence}); + sequences << defSequence; + } + for (const auto& assoc : entry->autoTypeAssociations()->getAll()) { + if (!sequences.contains(assoc.sequence) && !assoc.sequence.isEmpty()) { + matches.append({entry, assoc.sequence}); + sequences << assoc.sequence; + } + } + } + } + + m_ui->view->setMatchList(matches); +} + +void AutoTypeSelectDialog::moveSelectionUp() +{ + auto current = m_ui->view->currentIndex(); + auto previous = current.sibling(current.row() - 1, 0); + + if (previous.isValid()) { + m_ui->view->setCurrentIndex(previous); + } +} + +void AutoTypeSelectDialog::moveSelectionDown() +{ + auto current = m_ui->view->currentIndex(); + auto next = current.sibling(current.row() + 1, 0); + + if (next.isValid()) { + m_ui->view->setCurrentIndex(next); + } +} + +void AutoTypeSelectDialog::activateCurrentMatch() +{ + submitAutoTypeMatch(m_ui->view->currentMatch()); +} + +bool AutoTypeSelectDialog::eventFilter(QObject* obj, QEvent* event) +{ + if (obj == m_ui->action) { + if (event->type() == QEvent::FocusIn) { + m_ui->action->showMenu(); + return true; + } else if (event->type() == QEvent::KeyPress && static_cast(event)->key() == Qt::Key_Return) { + // handle case where the menu is closed but the button has focus + activateCurrentMatch(); + return true; + } + } else if (obj == m_actionMenu) { + if (event->type() == QEvent::KeyPress) { + auto* keyEvent = static_cast(event); + switch (keyEvent->key()) { + case Qt::Key_Tab: + m_actionMenu->close(); + focusNextPrevChild(true); + return true; + case Qt::Key_Backtab: + m_actionMenu->close(); + focusNextPrevChild(false); + return true; + case Qt::Key_Return: + // accept the dialog with default sequence if no action selected + if (!m_actionMenu->activeAction()) { + activateCurrentMatch(); + return true; + } + default: + break; + } + } + } else if (obj == m_ui->search) { + if (event->type() == QEvent::KeyPress) { + auto* keyEvent = static_cast(event); + switch (keyEvent->key()) { + case Qt::Key_Up: + moveSelectionUp(); + return true; + case Qt::Key_Down: + moveSelectionDown(); + return true; + case Qt::Key_Escape: + if (m_ui->search->text().isEmpty()) { + reject(); + } else { + m_ui->search->clear(); + } + return true; + default: + break; + } + } + } + + return QDialog::eventFilter(obj, event); +} + +void AutoTypeSelectDialog::updateActionMenu(const AutoTypeMatch& match) +{ + if (!match.first) { + m_ui->action->setEnabled(false); + return; + } + + m_ui->action->setEnabled(true); + + bool hasUsername = !match.first->username().isEmpty(); + bool hasPassword = !match.first->password().isEmpty(); + bool hasTotp = match.first->hasTotp(); + + auto actions = m_actionMenu->actions(); + Q_ASSERT(actions.size() >= 6); + actions[0]->setEnabled(hasUsername); + actions[1]->setEnabled(hasPassword); + actions[2]->setEnabled(hasTotp); + actions[3]->setEnabled(hasUsername); + actions[4]->setEnabled(hasPassword); + actions[5]->setEnabled(hasTotp); +} + +void AutoTypeSelectDialog::buildActionMenu() +{ + m_actionMenu = new QMenu(this); + auto typeUsernameAction = new QAction(icons()->icon("auto-type"), tr("Type {USERNAME}"), this); + auto typePasswordAction = new QAction(icons()->icon("auto-type"), tr("Type {PASSWORD}"), this); + auto typeTotpAction = new QAction(icons()->icon("auto-type"), tr("Type {TOTP}"), this); + auto copyUsernameAction = new QAction(icons()->icon("username-copy"), tr("Copy Username"), this); + auto copyPasswordAction = new QAction(icons()->icon("password-copy"), tr("Copy Password"), this); + auto copyTotpAction = new QAction(icons()->icon("chronometer"), tr("Copy TOTP"), this); + m_actionMenu->addAction(typeUsernameAction); + m_actionMenu->addAction(typePasswordAction); + m_actionMenu->addAction(typeTotpAction); + m_actionMenu->addAction(copyUsernameAction); + m_actionMenu->addAction(copyPasswordAction); + m_actionMenu->addAction(copyTotpAction); + + connect(typeUsernameAction, &QAction::triggered, this, [&] { + auto match = m_ui->view->currentMatch(); + match.second = "{USERNAME}"; + submitAutoTypeMatch(match); + }); + connect(typePasswordAction, &QAction::triggered, this, [&] { + auto match = m_ui->view->currentMatch(); + match.second = "{PASSWORD}"; + submitAutoTypeMatch(match); + }); + connect(typeTotpAction, &QAction::triggered, this, [&] { + auto match = m_ui->view->currentMatch(); + match.second = "{TOTP}"; + submitAutoTypeMatch(match); + }); + + connect(copyUsernameAction, &QAction::triggered, this, [&] { + clipboard()->setText(m_ui->view->currentMatch().first->username()); + reject(); + }); + connect(copyPasswordAction, &QAction::triggered, this, [&] { + clipboard()->setText(m_ui->view->currentMatch().first->password()); + reject(); + }); + connect(copyTotpAction, &QAction::triggered, this, [&] { + clipboard()->setText(m_ui->view->currentMatch().first->totp()); + reject(); + }); +} + +void AutoTypeSelectDialog::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - QRect screenGeometry = QApplication::screenAt(QCursor::pos())->availableGeometry(); + auto screen = QApplication::screenAt(QCursor::pos()); + if (!screen) { + // screenAt can return a nullptr, default to the primary screen + screen = QApplication::primaryScreen(); + } + QRect screenGeometry = screen->availableGeometry(); #else QRect screenGeometry = QApplication::desktop()->availableGeometry(QCursor::pos()); #endif + + // Resize to last used size QSize size = config()->get(Config::GUI_AutoTypeSelectDialogSize).toSize(); size.setWidth(qMin(size.width(), screenGeometry.width())); size.setHeight(qMin(size.height(), screenGeometry.height())); resize(size); // move dialog to the center of the screen - QPoint screenCenter = screenGeometry.center(); - move(screenCenter.x() - (size.width() / 2), screenCenter.y() - (size.height() / 2)); - - QVBoxLayout* layout = new QVBoxLayout(this); - - QLabel* descriptionLabel = new QLabel(tr("Select entry to Auto-Type:"), this); - layout->addWidget(descriptionLabel); - - // clang-format off - connect(m_view, SIGNAL(activated(QModelIndex)), SLOT(emitMatchActivated(QModelIndex))); - connect(m_view, SIGNAL(clicked(QModelIndex)), SLOT(emitMatchActivated(QModelIndex))); - connect(m_view->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(matchRemoved())); - connect(m_view, SIGNAL(rejected()), SLOT(reject())); - connect(m_view, SIGNAL(matchTextCopied()), SLOT(reject())); - // clang-format on - - QSortFilterProxyModel* proxy = qobject_cast(m_view->model()); - if (proxy) { - proxy->setFilterKeyColumn(-1); - proxy->setFilterCaseSensitivity(Qt::CaseInsensitive); - } - - layout->addWidget(m_view); - - connect(m_filterLineEdit, SIGNAL(textChanged(QString)), SLOT(filterList(QString))); - connect(m_filterLineEdit, SIGNAL(returnPressed()), SLOT(activateCurrentIndex())); - connect(m_filterLineEdit, SIGNAL(keyUpPressed()), SLOT(moveSelectionUp())); - connect(m_filterLineEdit, SIGNAL(keyDownPressed()), SLOT(moveSelectionDown())); - connect(m_filterLineEdit, SIGNAL(escapeReleased()), SLOT(reject())); - - m_filterLineEdit->setPlaceholderText(tr("Search…")); - layout->addWidget(m_filterLineEdit); - - QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, Qt::Horizontal, this); - connect(buttonBox, SIGNAL(rejected()), SLOT(reject())); - layout->addWidget(buttonBox); - - m_filterLineEdit->setFocus(); + move(screenGeometry.center().x() - (size.width() / 2), screenGeometry.center().y() - (size.height() / 2)); } -void AutoTypeSelectDialog::setMatchList(const QList& matchList) -{ - m_view->setMatchList(matchList); - - m_view->header()->resizeSections(QHeaderView::ResizeToContents); -} - -void AutoTypeSelectDialog::done(int r) +void AutoTypeSelectDialog::hideEvent(QHideEvent* event) { config()->set(Config::GUI_AutoTypeSelectDialogSize, size()); - - QDialog::done(r); -} - -void AutoTypeSelectDialog::reject() -{ - m_rejected = true; - - QDialog::reject(); -} - -void AutoTypeSelectDialog::emitMatchActivated(const QModelIndex& index) -{ - // make sure we don't emit the signal twice when both activated() and clicked() are triggered - if (m_matchActivatedEmitted) { - return; + if (!m_accepted) { + emit rejected(); } - m_matchActivatedEmitted = true; - - AutoTypeMatch match = m_view->matchFromIndex(index); - accept(); - emit matchActivated(match); -} - -void AutoTypeSelectDialog::matchRemoved() -{ - if (m_rejected) { - return; - } - - if (m_view->model()->rowCount() == 0 && m_filterLineEdit->text().isEmpty()) { - reject(); - } -} - -void AutoTypeSelectDialog::filterList(QString filterString) -{ - QSortFilterProxyModel* proxy = qobject_cast(m_view->model()); - if (proxy) { - proxy->setFilterWildcard(filterString); - if (!m_view->currentIndex().isValid()) { - m_view->setCurrentIndex(m_view->model()->index(0, 0)); - } - } -} - -void AutoTypeSelectDialog::moveSelectionUp() -{ - auto current = m_view->currentIndex(); - auto previous = current.sibling(current.row() - 1, 0); - - if (previous.isValid()) { - m_view->setCurrentIndex(previous); - } -} - -void AutoTypeSelectDialog::moveSelectionDown() -{ - auto current = m_view->currentIndex(); - auto next = current.sibling(current.row() + 1, 0); - - if (next.isValid()) { - m_view->setCurrentIndex(next); - } -} - -void AutoTypeSelectDialog::activateCurrentIndex() -{ - emitMatchActivated(m_view->currentIndex()); + QDialog::hideEvent(event); } diff --git a/src/autotype/AutoTypeSelectDialog.h b/src/autotype/AutoTypeSelectDialog.h index a22218f1f..2adf67424 100644 --- a/src/autotype/AutoTypeSelectDialog.h +++ b/src/autotype/AutoTypeSelectDialog.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2021 Team KeePassXC * Copyright (C) 2012 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -18,14 +19,17 @@ #ifndef KEEPASSX_AUTOTYPESELECTDIALOG_H #define KEEPASSX_AUTOTYPESELECTDIALOG_H -#include +#include "autotype/AutoTypeMatch.h" #include -#include +#include -#include "autotype/AutoTypeFilterLineEdit.h" -#include "core/AutoTypeMatch.h" +class Database; +class QMenu; -class AutoTypeSelectView; +namespace Ui +{ + class AutoTypeSelectDialog; +} class AutoTypeSelectDialog : public QDialog { @@ -33,28 +37,37 @@ class AutoTypeSelectDialog : public QDialog public: explicit AutoTypeSelectDialog(QWidget* parent = nullptr); - void setMatchList(const QList& matchList); + ~AutoTypeSelectDialog() override; + + void setMatches(const QList& matchList, const QList>& dbs); signals: void matchActivated(AutoTypeMatch match); -public slots: - void done(int r) override; - void reject() override; +protected: + bool eventFilter(QObject* obj, QEvent* event) override; + void showEvent(QShowEvent* event) override; + void hideEvent(QHideEvent* event) override; private slots: - void emitMatchActivated(const QModelIndex& index); - void matchRemoved(); - void filterList(QString filterString); + void submitAutoTypeMatch(AutoTypeMatch match); + void performSearch(); void moveSelectionUp(); void moveSelectionDown(); - void activateCurrentIndex(); + void activateCurrentMatch(); + void updateActionMenu(const AutoTypeMatch& match); private: - AutoTypeSelectView* const m_view; - AutoTypeFilterLineEdit* const m_filterLineEdit; - bool m_matchActivatedEmitted; - bool m_rejected; + void buildActionMenu(); + + QScopedPointer m_ui; + + QList> m_dbs; + QList m_matches; + QTimer m_searchTimer; + QPointer m_actionMenu; + + bool m_accepted = false; }; #endif // KEEPASSX_AUTOTYPESELECTDIALOG_H diff --git a/src/autotype/AutoTypeSelectDialog.ui b/src/autotype/AutoTypeSelectDialog.ui new file mode 100644 index 000000000..2f77bd2b2 --- /dev/null +++ b/src/autotype/AutoTypeSelectDialog.ui @@ -0,0 +1,197 @@ + + + AutoTypeSelectDialog + + + + 0 + 0 + 418 + 295 + + + + Auto-Type - KeePassXC + + + + + + Double click a row to perform Auto-Type or find an entry using the search: + + + + + + + + 0 + 0 + + + + + 0 + 175 + + + + PointingHandCursor + + + false + + + false + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + true + + + false + + + + + + + + 10 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + &Filter Matches + + + true + + + + + + + &Search Database + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + + + + + 400 + 0 + + + + Filter or Search… + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Type Sequence + + + QToolButton::MenuButtonPopup + + + + + + + Cancel + + + false + + + + + + + + + + AutoTypeMatchView + QTableView +

autotype/AutoTypeMatchView.h
+ + + + view + filterRadio + searchRadio + search + + + + diff --git a/src/autotype/AutoTypeSelectView.cpp b/src/autotype/AutoTypeSelectView.cpp deleted file mode 100644 index cfa113671..000000000 --- a/src/autotype/AutoTypeSelectView.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2012 Felix Geyer - * - * 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 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "AutoTypeSelectView.h" - -#include -#include - -AutoTypeSelectView::AutoTypeSelectView(QWidget* parent) - : AutoTypeMatchView(parent) -{ - setMouseTracking(true); - setAllColumnsShowFocus(true); - - connect(model(), SIGNAL(modelReset()), SLOT(selectFirstMatch())); -} - -void AutoTypeSelectView::mouseMoveEvent(QMouseEvent* event) -{ - QModelIndex index = indexAt(event->pos()); - - if (index.isValid()) { - setCurrentIndex(index); - setCursor(Qt::PointingHandCursor); - } else { - unsetCursor(); - } - - AutoTypeMatchView::mouseMoveEvent(event); -} - -void AutoTypeSelectView::selectFirstMatch() -{ - QModelIndex index = model()->index(0, 0); - - if (index.isValid()) { - setCurrentIndex(index); - } -} - -void AutoTypeSelectView::keyReleaseEvent(QKeyEvent* e) -{ - if (e->key() == Qt::Key_Escape) { - emit rejected(); - } else { - e->ignore(); - } -} \ No newline at end of file diff --git a/src/autotype/AutoTypeSelectView.h b/src/autotype/AutoTypeSelectView.h deleted file mode 100644 index e6a2ec652..000000000 --- a/src/autotype/AutoTypeSelectView.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2012 Felix Geyer - * - * 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 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSX_AUTOTYPESELECTVIEW_H -#define KEEPASSX_AUTOTYPESELECTVIEW_H - -#include "gui/entry/AutoTypeMatchView.h" - -class AutoTypeSelectView : public AutoTypeMatchView -{ - Q_OBJECT - -public: - explicit AutoTypeSelectView(QWidget* parent = nullptr); - -protected: - void mouseMoveEvent(QMouseEvent* event) override; - void keyReleaseEvent(QKeyEvent* e) override; - -private slots: - void selectFirstMatch(); - -signals: - void rejected(); -}; - -#endif // KEEPASSX_AUTOTYPESELECTVIEW_H diff --git a/src/autotype/WildcardMatcher.cpp b/src/autotype/WildcardMatcher.cpp deleted file mode 100644 index b69425ee1..000000000 --- a/src/autotype/WildcardMatcher.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2012 Felix Geyer - * - * 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 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "WildcardMatcher.h" - -#include -#include - -const QChar WildcardMatcher::Wildcard = '*'; -const Qt::CaseSensitivity WildcardMatcher::Sensitivity = Qt::CaseInsensitive; - -WildcardMatcher::WildcardMatcher(QString text) - : m_text(std::move(text)) -{ -} - -bool WildcardMatcher::match(const QString& pattern) -{ - m_pattern = pattern; - - if (patternContainsWildcard()) { - return matchWithWildcards(); - } else { - return patternEqualsText(); - } -} - -bool WildcardMatcher::patternContainsWildcard() -{ - return m_pattern.contains(Wildcard); -} - -bool WildcardMatcher::patternEqualsText() -{ - return m_text.compare(m_pattern, Sensitivity) == 0; -} - -bool WildcardMatcher::matchWithWildcards() -{ - QStringList parts = m_pattern.split(Wildcard, QString::KeepEmptyParts); - Q_ASSERT(parts.size() >= 2); - - if (startOrEndDoesNotMatch(parts)) { - return false; - } - - return partsMatch(parts); -} - -bool WildcardMatcher::startOrEndDoesNotMatch(const QStringList& parts) -{ - return !m_text.startsWith(parts.first(), Sensitivity) || !m_text.endsWith(parts.last(), Sensitivity); -} - -bool WildcardMatcher::partsMatch(const QStringList& parts) -{ - int index = 0; - for (const QString& part : parts) { - int matchIndex = getMatchIndex(part, index); - if (noMatchFound(matchIndex)) { - return false; - } - index = calculateNewIndex(matchIndex, part.length()); - } - - return true; -} - -int WildcardMatcher::getMatchIndex(const QString& part, int startIndex) -{ - return m_text.indexOf(part, startIndex, Sensitivity); -} - -bool WildcardMatcher::noMatchFound(int index) -{ - return index == -1; -} - -int WildcardMatcher::calculateNewIndex(int matchIndex, int partLength) -{ - return matchIndex + partLength; -} diff --git a/src/autotype/WildcardMatcher.h b/src/autotype/WildcardMatcher.h deleted file mode 100644 index d8ee1dc79..000000000 --- a/src/autotype/WildcardMatcher.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2012 Felix Geyer - * - * 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 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSX_WILDCARDMATCHER_H -#define KEEPASSX_WILDCARDMATCHER_H - -#include - -class WildcardMatcher -{ -public: - explicit WildcardMatcher(QString text); - bool match(const QString& pattern); - - static const QChar Wildcard; - -private: - bool patternEqualsText(); - bool patternContainsWildcard(); - bool matchWithWildcards(); - bool startOrEndDoesNotMatch(const QStringList& parts); - bool partsMatch(const QStringList& parts); - int getMatchIndex(const QString& part, int startIndex); - bool noMatchFound(int index); - int calculateNewIndex(int matchIndex, int partLength); - - static const Qt::CaseSensitivity Sensitivity; - const QString m_text; - QString m_pattern; -}; - -#endif // KEEPASSX_WILDCARDMATCHER_H diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index ae6d9f1f5..69367fbac 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -70,6 +70,10 @@ BrowserService::BrowserService() , m_keepassBrowserUUID(Tools::hexToUuid("de887cc3036343b8974b5911b8816224")) { connect(m_browserHost, &BrowserHost::clientMessageReceived, this, &BrowserService::processClientMessage); + connect(getMainWindow(), &MainWindow::databaseUnlocked, this, &BrowserService::databaseUnlocked); + connect(getMainWindow(), &MainWindow::databaseLocked, this, &BrowserService::databaseLocked); + connect(getMainWindow(), &MainWindow::activeDatabaseChanged, this, &BrowserService::activeDatabaseChanged); + setEnabled(browserSettings()->isEnabled()); } diff --git a/src/core/AutoTypeMatch.cpp b/src/core/AutoTypeMatch.cpp deleted file mode 100644 index 13b037bb6..000000000 --- a/src/core/AutoTypeMatch.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2015 David Wu - * Copyright (C) 2017 KeePassXC Team - * - * 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 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "AutoTypeMatch.h" - -#include - -AutoTypeMatch::AutoTypeMatch() - : entry(nullptr) - , sequence() -{ -} - -AutoTypeMatch::AutoTypeMatch(Entry* entry, QString sequence) - : entry(entry) - , sequence(std::move(sequence)) -{ -} - -bool AutoTypeMatch::operator==(const AutoTypeMatch& other) const -{ - return entry == other.entry && sequence == other.sequence; -} - -bool AutoTypeMatch::operator!=(const AutoTypeMatch& other) const -{ - return entry != other.entry || sequence != other.sequence; -} diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index c749b03e4..61f48b780 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -20,6 +20,7 @@ #include "config-keepassx.h" #include "core/Clock.h" +#include "core/Config.h" #include "core/Database.h" #include "core/DatabaseIcons.h" #include "core/Group.h" @@ -280,6 +281,75 @@ QString Entry::effectiveAutoTypeSequence() const return sequence; } +/** + * Retrive the autotype sequences matches for a given windowTitle + * This returns a list with priority ordering. If you don't want duplicates call .toSet() on it. + */ +QList Entry::autoTypeSequences(const QString& windowTitle) const +{ + // If no window just return the effective sequence + if (windowTitle.isEmpty()) { + return {effectiveAutoTypeSequence()}; + } + + // Define helper functions to match window titles + auto windowMatches = [&](const QString& pattern) { + // Regex searching + if (pattern.startsWith("//") && pattern.endsWith("//") && pattern.size() >= 4) { + QRegExp regExp(pattern.mid(2, pattern.size() - 4), Qt::CaseInsensitive, QRegExp::RegExp2); + return (regExp.indexIn(windowTitle) != -1); + } + + // Wildcard searching + auto regex = Tools::convertToRegex(pattern, true, false, false); + return windowTitle.contains(regex); + }; + + auto windowMatchesTitle = [&](const QString& entryTitle) { + return !entryTitle.isEmpty() && windowTitle.contains(entryTitle, Qt::CaseInsensitive); + }; + + auto windowMatchesUrl = [&](const QString& entryUrl) { + if (!entryUrl.isEmpty() && windowTitle.contains(entryUrl, Qt::CaseInsensitive)) { + return true; + } + + QUrl url(entryUrl); + if (url.isValid() && !url.host().isEmpty()) { + return windowTitle.contains(url.host(), Qt::CaseInsensitive); + } + + return false; + }; + + QList sequenceList; + + // Add window association matches + const auto assocList = autoTypeAssociations()->getAll(); + for (const auto& assoc : assocList) { + auto window = resolveMultiplePlaceholders(assoc.window); + if (windowMatches(window)) { + if (!assoc.sequence.isEmpty()) { + sequenceList << assoc.sequence; + } else { + sequenceList << effectiveAutoTypeSequence(); + } + } + } + + // Try to match window title + if (config()->get(Config::AutoTypeEntryTitleMatch).toBool() && windowMatchesTitle(resolvePlaceholder(title()))) { + sequenceList << effectiveAutoTypeSequence(); + } + + // Try to match url in window title + if (config()->get(Config::AutoTypeEntryURLMatch).toBool() && windowMatchesUrl(resolvePlaceholder(url()))) { + sequenceList << effectiveAutoTypeSequence(); + } + + return sequenceList; +} + AutoTypeAssociations* Entry::autoTypeAssociations() { return m_autoTypeAssociations; diff --git a/src/core/Entry.h b/src/core/Entry.h index a86aded10..be666b9a0 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -95,6 +95,7 @@ public: QString defaultAutoTypeSequence() const; QString effectiveAutoTypeSequence() const; QString effectiveNewAutoTypeSequence() const; + QList autoTypeSequences(const QString& pattern = {}) const; AutoTypeAssociations* autoTypeAssociations(); const AutoTypeAssociations* autoTypeAssociations() const; QString title() const; diff --git a/src/fdosecrets/objects/Service.cpp b/src/fdosecrets/objects/Service.cpp index 38dc0aff6..603aaba1e 100644 --- a/src/fdosecrets/objects/Service.cpp +++ b/src/fdosecrets/objects/Service.cpp @@ -82,7 +82,7 @@ namespace FdoSecrets }); // make default alias track current activated database - connect(m_databases.data(), &DatabaseTabWidget::activateDatabaseChanged, this, &Service::ensureDefaultAlias); + connect(m_databases.data(), &DatabaseTabWidget::activeDatabaseChanged, this, &Service::ensureDefaultAlias); return true; } diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 2683cecec..6f79873af 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -57,8 +57,8 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) // clang-format off connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabaseTab(int))); - connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged())); - connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), + connect(this, SIGNAL(currentChanged(int)), SLOT(emitActiveDatabaseChanged())); + connect(this, SIGNAL(activeDatabaseChanged(DatabaseWidget*)), m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*))); connect(autoType(), SIGNAL(globalAutoTypeTriggered()), SLOT(performGlobalAutoType())); connect(autoType(), SIGNAL(autotypePerformed()), SLOT(relockPendingDatabase())); @@ -715,9 +715,9 @@ void DatabaseTabWidget::updateLastDatabases(const QString& filename) } } -void DatabaseTabWidget::emitActivateDatabaseChanged() +void DatabaseTabWidget::emitActiveDatabaseChanged() { - emit activateDatabaseChanged(currentDatabaseWidget()); + emit activeDatabaseChanged(currentDatabaseWidget()); } void DatabaseTabWidget::emitDatabaseLockChanged() diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index e59681ea7..fbb6411f8 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -89,7 +89,7 @@ signals: void databaseClosed(const QString& filePath); void databaseUnlocked(DatabaseWidget* dbWidget); void databaseLocked(DatabaseWidget* dbWidget); - void activateDatabaseChanged(DatabaseWidget* dbWidget); + void activeDatabaseChanged(DatabaseWidget* dbWidget); void tabNameChanged(); void tabVisibilityChanged(bool tabsVisible); void messageGlobal(const QString&, MessageWidget::MessageType type); @@ -98,7 +98,7 @@ signals: private slots: void toggleTabbar(); - void emitActivateDatabaseChanged(); + void emitActiveDatabaseChanged(); void emitDatabaseLockChanged(); private: diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index de19f37a9..5b40a9b8e 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -160,14 +160,13 @@ MainWindow::MainWindow() restoreGeometry(config()->get(Config::GUI_MainWindowGeometry).toByteArray()); restoreState(config()->get(Config::GUI_MainWindowState).toByteArray()); + + connect(m_ui->tabWidget, &DatabaseTabWidget::databaseLocked, this, &MainWindow::databaseLocked); + connect(m_ui->tabWidget, &DatabaseTabWidget::databaseUnlocked, this, &MainWindow::databaseUnlocked); + connect(m_ui->tabWidget, &DatabaseTabWidget::activeDatabaseChanged, this, &MainWindow::activeDatabaseChanged); + #ifdef WITH_XC_BROWSER m_ui->settingsWidget->addSettingsPage(new BrowserSettingsPage()); - connect(m_ui->tabWidget, &DatabaseTabWidget::databaseLocked, browserService(), &BrowserService::databaseLocked); - connect(m_ui->tabWidget, &DatabaseTabWidget::databaseUnlocked, browserService(), &BrowserService::databaseUnlocked); - connect(m_ui->tabWidget, - &DatabaseTabWidget::activateDatabaseChanged, - browserService(), - &BrowserService::activeDatabaseChanged); connect( browserService(), &BrowserService::requestUnlock, m_ui->tabWidget, &DatabaseTabWidget::performBrowserUnlock); #endif @@ -411,7 +410,7 @@ MainWindow::MainWindow() // Notify search when the active database changes or gets locked connect(m_ui->tabWidget, - SIGNAL(activateDatabaseChanged(DatabaseWidget*)), + SIGNAL(activeDatabaseChanged(DatabaseWidget*)), m_searchWidget, SLOT(databaseChanged(DatabaseWidget*))); connect(m_ui->tabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), m_searchWidget, SLOT(databaseChanged())); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 63163c8d2..13e59b5ae 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -60,6 +60,11 @@ public: PasswordGeneratorScreen = 3 }; +signals: + void databaseUnlocked(DatabaseWidget* dbWidget); + void databaseLocked(DatabaseWidget* dbWidget); + void activeDatabaseChanged(DatabaseWidget* dbWidget); + public slots: void openDatabase(const QString& filePath, const QString& password = {}, const QString& keyfile = {}); void appExit(); @@ -136,8 +141,6 @@ private slots: void obtainContextFocusLock(); void releaseContextFocusLock(); void agentEnabled(bool enabled); - -private slots: void updateTrayIcon(); private: diff --git a/src/gui/SearchHelpWidget.ui b/src/gui/SearchHelpWidget.ui index ebc62e991..bd8731eb9 100644 --- a/src/gui/SearchHelpWidget.ui +++ b/src/gui/SearchHelpWidget.ui @@ -118,7 +118,7 @@ - + 10 diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp index 7daea45d8..787b544ce 100644 --- a/src/gui/SearchWidget.cpp +++ b/src/gui/SearchWidget.cpp @@ -51,8 +51,8 @@ SearchWidget::SearchWidget(QWidget* parent) connect(m_clearSearchTimer, SIGNAL(timeout()), m_ui->searchEdit, SLOT(clear())); connect(this, SIGNAL(escapePressed()), m_ui->searchEdit, SLOT(clear())); - new QShortcut(QKeySequence::Find, this, SLOT(searchFocus()), nullptr, Qt::ApplicationShortcut); - new QShortcut(Qt::Key_Escape, m_ui->searchEdit, SLOT(clear()), nullptr, Qt::ApplicationShortcut); + new QShortcut(QKeySequence::Find, this, SLOT(searchFocus())); + new QShortcut(Qt::Key_Escape, m_ui->searchEdit, SLOT(clear())); m_ui->searchEdit->setPlaceholderText(tr("Search (%1)…", "Search placeholder text, %1 is the keyboard shortcut") .arg(QKeySequence(QKeySequence::Find).toString(QKeySequence::NativeText))); diff --git a/src/gui/entry/AutoTypeMatchView.cpp b/src/gui/entry/AutoTypeMatchView.cpp deleted file mode 100644 index 72ee32fde..000000000 --- a/src/gui/entry/AutoTypeMatchView.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2015 David Wu - * Copyright (C) 2017 KeePassXC Team - * - * 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 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "AutoTypeMatchView.h" - -#include "core/Entry.h" -#include "gui/Clipboard.h" -#include "gui/SortFilterHideProxyModel.h" - -#include -#include -#include - -AutoTypeMatchView::AutoTypeMatchView(QWidget* parent) - : QTreeView(parent) - , m_model(new AutoTypeMatchModel(this)) - , m_sortModel(new SortFilterHideProxyModel(this)) -{ - m_sortModel->setSourceModel(m_model); - m_sortModel->setDynamicSortFilter(true); - m_sortModel->setSortLocaleAware(true); - m_sortModel->setSortCaseSensitivity(Qt::CaseInsensitive); - setModel(m_sortModel); - - setUniformRowHeights(true); - setRootIsDecorated(false); - setAlternatingRowColors(true); - setDragEnabled(false); - setSortingEnabled(true); - setSelectionMode(QAbstractItemView::SingleSelection); - header()->setDefaultSectionSize(150); - - setContextMenuPolicy(Qt::ActionsContextMenu); - auto* copyUserNameAction = new QAction(tr("Copy &username"), this); - auto* copyPasswordAction = new QAction(tr("Copy &password"), this); - addAction(copyUserNameAction); - addAction(copyPasswordAction); - - connect(copyUserNameAction, SIGNAL(triggered()), this, SLOT(userNameCopied())); - connect(copyPasswordAction, SIGNAL(triggered()), this, SLOT(passwordCopied())); - - connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitMatchActivated(QModelIndex))); - // clang-format off - connect(selectionModel(), - SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - SIGNAL(matchSelectionChanged())); - // clang-format on -} - -void AutoTypeMatchView::userNameCopied() -{ - clipboard()->setText(currentMatch().entry->username()); - emit matchTextCopied(); -} - -void AutoTypeMatchView::passwordCopied() -{ - clipboard()->setText(currentMatch().entry->password()); - emit matchTextCopied(); -} - -void AutoTypeMatchView::keyPressEvent(QKeyEvent* event) -{ - if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) { - emitMatchActivated(currentIndex()); -#ifdef Q_OS_MACOS - // Pressing return does not emit the QTreeView::activated signal on mac os - emit activated(currentIndex()); -#endif - } - - QTreeView::keyPressEvent(event); -} - -void AutoTypeMatchView::setMatchList(const QList& matches) -{ - m_model->setMatchList(matches); - - bool sameSequences = true; - if (matches.count() > 1) { - QString sequenceTest = matches[0].sequence; - for (const auto& match : matches) { - if (match.sequence != sequenceTest) { - sameSequences = false; - break; - } - } - } - setColumnHidden(AutoTypeMatchModel::Sequence, sameSequences); - - for (int i = 0; i < m_model->columnCount(); ++i) { - resizeColumnToContents(i); - if (columnWidth(i) > 250) { - setColumnWidth(i, 250); - } - } - - setFirstMatchActive(); -} - -void AutoTypeMatchView::setFirstMatchActive() -{ - if (m_model->rowCount() > 0) { - QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0)); - setCurrentMatch(m_model->matchFromIndex(index)); - } else { - emit matchSelectionChanged(); - } -} - -void AutoTypeMatchView::emitMatchActivated(const QModelIndex& index) -{ - AutoTypeMatch match = matchFromIndex(index); - - emit matchActivated(match); -} - -AutoTypeMatch AutoTypeMatchView::currentMatch() -{ - QModelIndexList list = selectionModel()->selectedRows(); - if (list.size() == 1) { - return m_model->matchFromIndex(m_sortModel->mapToSource(list.first())); - } - return AutoTypeMatch(); -} - -void AutoTypeMatchView::setCurrentMatch(const AutoTypeMatch& match) -{ - selectionModel()->setCurrentIndex(m_sortModel->mapFromSource(m_model->indexFromMatch(match)), - QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); -} - -AutoTypeMatch AutoTypeMatchView::matchFromIndex(const QModelIndex& index) -{ - if (index.isValid()) { - return m_model->matchFromIndex(m_sortModel->mapToSource(index)); - } - return AutoTypeMatch(); -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 28d8516cf..b532acba9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -149,9 +149,6 @@ add_unit_test(NAME testkeepass1reader SOURCES TestKeePass1Reader.cpp add_unit_test(NAME testopvaultreader SOURCES TestOpVaultReader.cpp LIBS ${TEST_LIBRARIES}) -add_unit_test(NAME testwildcardmatcher SOURCES TestWildcardMatcher.cpp - LIBS ${TEST_LIBRARIES}) - if(WITH_XC_NETWORKING) add_unit_test(NAME testupdatecheck SOURCES TestUpdateCheck.cpp LIBS ${TEST_LIBRARIES}) diff --git a/tests/TestWildcardMatcher.cpp b/tests/TestWildcardMatcher.cpp deleted file mode 100644 index b7b92fdd7..000000000 --- a/tests/TestWildcardMatcher.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2012 Felix Geyer - * - * 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 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "TestWildcardMatcher.h" -#include "TestGlobal.h" -#include "autotype/WildcardMatcher.h" - -QTEST_GUILESS_MAIN(TestWildcardMatcher) - -const QString TestWildcardMatcher::DefaultText = QString("some text"); -const QString TestWildcardMatcher::AlternativeText = QString("some other text"); - -void TestWildcardMatcher::testMatcher_data() -{ - QTest::addColumn("text"); - QTest::addColumn("pattern"); - QTest::addColumn("match"); - - QTest::newRow("MatchWithoutWildcard") << DefaultText << DefaultText << true; - QTest::newRow("NoMatchWithoutWildcard") << DefaultText << QString("no match text") << false; - QTest::newRow("MatchWithOneWildcardInTheMiddle") << AlternativeText << QString("some*text") << true; - QTest::newRow("NoMatchWithOneWildcardInTheMiddle") << AlternativeText << QString("differen*text") << false; - QTest::newRow("MatchWithOneWildcardAtBegin") << DefaultText << QString("*text") << true; - QTest::newRow("NoMatchWithOneWildcardAtBegin") << DefaultText << QString("*text something else") << false; - QTest::newRow("NatchWithOneWildcardAtEnd") << DefaultText << QString("some*") << true; - QTest::newRow("NoMatchWithOneWildcardAtEnd") << DefaultText << QString("some other*") << false; - QTest::newRow("MatchWithMultipleWildcards") << AlternativeText << QString("some*th*text") << true; - QTest::newRow("NoMatchWithMultipleWildcards") << AlternativeText << QString("some*abc*text") << false; - QTest::newRow("MatchJustWildcard") << DefaultText << QString("*") << true; - QTest::newRow("MatchFollowingWildcards") << DefaultText << QString("some t**t") << true; - QTest::newRow("CaseSensitivity") << DefaultText.toUpper() << QString("some t**t") << true; -} - -void TestWildcardMatcher::testMatcher() -{ - QFETCH(QString, text); - QFETCH(QString, pattern); - QFETCH(bool, match); - - initMatcher(text); - verifyMatchResult(pattern, match); - cleanupMatcher(); -} - -void TestWildcardMatcher::initMatcher(QString text) -{ - m_matcher = new WildcardMatcher(text); -} - -void TestWildcardMatcher::cleanupMatcher() -{ - delete m_matcher; -} - -void TestWildcardMatcher::verifyMatchResult(QString pattern, bool expected) -{ - if (expected) { - verifyMatch(pattern); - } else { - verifyNoMatch(pattern); - } -} - -void TestWildcardMatcher::verifyMatch(QString pattern) -{ - bool matchResult = m_matcher->match(pattern); - QVERIFY(matchResult); -} - -void TestWildcardMatcher::verifyNoMatch(QString pattern) -{ - bool matchResult = m_matcher->match(pattern); - QVERIFY(!matchResult); -} diff --git a/tests/TestWildcardMatcher.h b/tests/TestWildcardMatcher.h deleted file mode 100644 index e23770937..000000000 --- a/tests/TestWildcardMatcher.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2012 Felix Geyer - * - * 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 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSX_TESTWILDCARDMATCHER_H -#define KEEPASSX_TESTWILDCARDMATCHER_H - -#include - -class WildcardMatcher; - -class TestWildcardMatcher : public QObject -{ - Q_OBJECT - -private slots: - void testMatcher(); - void testMatcher_data(); - -private: - static const QString DefaultText; - static const QString AlternativeText; - - void initMatcher(QString text); - void cleanupMatcher(); - void verifyMatchResult(QString pattern, bool expected); - void verifyMatch(QString pattern); - void verifyNoMatch(QString pattern); - - WildcardMatcher* m_matcher; -}; - -#endif // KEEPASSX_TESTWILDCARDMATCHER_H