From e85879654ea0c2c67f423075c0f72cde43aa7d17 Mon Sep 17 00:00:00 2001 From: Anton Krotov Date: Thu, 2 Sep 2021 23:15:33 +0000 Subject: [PATCH] CEDIT: new version git-svn-id: svn://kolibrios.org@9174 a494cfbc-eb01-0410-851d-a64ba20cac60 --- programs/develop/cedit/CEDIT | Bin 31727 -> 34254 bytes programs/develop/cedit/CEDIT.INI | 9 +- programs/develop/cedit/SHORTCUT.TXT | 39 ++ programs/develop/cedit/SRC/CEdit.ob07 | 670 +++++++++++++++------- programs/develop/cedit/SRC/Clipboard.ob07 | 11 +- programs/develop/cedit/SRC/File.ob07 | 2 + programs/develop/cedit/SRC/Graph.ob07 | 48 +- programs/develop/cedit/SRC/Icons.ob07 | 2 +- programs/develop/cedit/SRC/Ini.ob07 | 34 +- programs/develop/cedit/SRC/KolibriOS.ob07 | 4 +- programs/develop/cedit/SRC/Languages.ob07 | 24 +- programs/develop/cedit/SRC/Lines.ob07 | 96 +++- programs/develop/cedit/SRC/Menu.ob07 | 211 +++++-- programs/develop/cedit/SRC/RW.ob07 | 34 +- programs/develop/cedit/SRC/Tabs.ob07 | 5 +- programs/develop/cedit/SRC/Text.ob07 | 347 ++++++++--- programs/develop/cedit/SRC/Timer.ob07 | 85 +++ programs/develop/cedit/SRC/libimg.ob07 | 2 +- programs/develop/cedit/SRC/scroll.ob07 | 416 ++++++++++---- 19 files changed, 1547 insertions(+), 492 deletions(-) create mode 100644 programs/develop/cedit/SHORTCUT.TXT create mode 100644 programs/develop/cedit/SRC/Timer.ob07 diff --git a/programs/develop/cedit/CEDIT b/programs/develop/cedit/CEDIT index 6b0474e70a4560b0d053666b3295cb12c96d70de..262986b7c891063ec80891804373bd27ec429abb 100644 GIT binary patch literal 34254 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 o~)lz$OI4&8Y z7KgV=%hO5?D4#`tLUF7ttizzvOJf1JQfWCwokdOIr0t$r4Ghn0AI+&YGt#uRuJNLf z!whS65+H6(F`F_0d<#_IbR1t)gCJn_%F8zUSFNCna>`P@yAuZm`d&U4Jcskw>cRm7BwR}D{)z>9DN3@j9zCjXn>JiH&(pff=3 zEiKqZXCGds=p#%nw;#!0?r5*E1es+Gb;>w$|)MB(Alx1C4&C8ZnfG_C?fR(&+*eVjW7ZgRFFIUV5XQLz5c(i9h6+Pw!2pKq}%RDC91{@rw@nFZGBbat`} zw?2T^o6==l=~Re_-u;dBui=Ov#S{ZtaQ0P)mKW|3s;*R&{U~iY3@eaWoR6Mvag#Og z=`L6B=x|q})8FWpG-a+1V-9KFWcMd46vw}ng^b{~H3NB56)oe+1nw6+VsUOk;#YlL zkL>{j{mq1$T?G=CGs8{EMR|61Lv=N135Fk}_F2*5n*BIY0x$Kq6h1T`&{dmhtIdeu zWc|^e-Hgc?q(j#v@X>!xtTA0D)-FBK!uM>zb0`=@V$W1H$QWk^QXtSy`-;JZc8`M! z68?x_{7=7HjcT~0c|nhVhhJg+@}9|Er}8w}Rn(Z@V59i{M5SgUeJ4L2B6YyJt{GVB z+!35Oaq&3?4}!M02u;Q5ZOa(hosd%f2pVY}SwCK3K#XQN!J_uSG`W=g4#b}lvP5?h zmFtpcK~t-P>s46F(pb?SrZ_q&2b%mM@ergE7EjpGZ8V!Msz8BGAZn(c)BoK%Rgz2g z?D=ZL_Kxcg0xS;lB7=Qwu{6_h@Q~WS4M~PhlP{O)37>Zi=CCP?Y0=n!2&iM;dyu|q zn}1sOfFURDev06sI6~>rf-9=>^Hnbdh&gsXPL7Esi!QB0Pgq)PpelHYm(x*_Q6zuP-CvdWpE3%?vwSgq7ZT@^3UtIbh8o2m9cVdnl(DGL z6JQ88>x|8tgdX>1SKtaIDW#wVF1#EUsiqJ0LdCuK(WaiA|NHV2Z)}p8oeP?RUI9g$sv zDI;%TT}~K5sam>0wufOuJLi>FabRF;!>n%8EiU*RP3OzO5;hlFscnkaX!R2qG+Y(B;Sb;F%0ZjB|AB)sM9HcToQ5YPC z%Tf+F9854Io8OsLdu^UX#w|D47HLWq?N8@!v_4wGbZwl}+D(PW8Bk$gI*Wp6u}v)I z>s6YK$A}-`jjGHU?y->`1krmJ!J{hqeKQ;-BQQ@iiU{73edY_DHK$^V)}06ws&z$` zRek~rawe=4SWvSM=T^|CVkhx_SsMa`Pk8wg(`DDa3RP0663+xwUW9HH{XC!i!<+pA ziZprz{OBE68eXcN$29^0++xy++Ynue)p~5vmojQ<9gz z60wz{ED`6cBGGP$yhUPG7VD{}l2$}@Rfk|Uo%9E@grl_aS>`6y?2Sx@T^NF{M=0}# zMJX~=F`hie9|*u!YWf;P=Rh_tR(;{JySIze+z|S%QL=yK#%zvxf&MIl)YXD*4QpA*fD&J9-MzO}CeXVnN z)Rg#nWXHi(*@*um_k#DVRy4%y6;SZsX39hhwf+tH-DS)RD8$0;kPrwCr*{L>p<=Yy z(o=8i&^vFV2|HC%!iGR&^FltOQw8o7L7(9nCe>1$3fsd*txAQ6UJdjL2|`~BRC1`N zZ2b^8h1h4t!S0BtXg_|~p~tXXvY&^6=l#g4IrqFH=3rB%5+eG^`ICpCjWRSc{>qXZ z{{5&BnINo|Sx?#$k?3|{7Jcz@=R?oL0sk&tGZ~vo-HkF`-!haWdC_i9btt8&|Jo89 z1jf6nz|$__?ZG)gQq%$K0X`LMVy0S7vSG=?l-s_O_vH&nMnZ zfQvg*4J(5%oVsPIO^co&?v3+ckJtXkXo2xcH7Z_VDHan49Wvc;|3xR+?<-Hgsh4hM zbPLcySM3DS|DOygavHc69nV0st+#0}bn@^gxpHn%5-hsJDYS^L@lkjO8)<|$RSThy z-zCXc@t!}X`2gwnEDywU#{Eu*XGK=vepj?NT!H}&x&G_w$?`VlPY)R{O8zw|tO6Ab zWq@!z{<=oFsmg7k9PBtqtZD@h$|a?Mktck@SIOE~k4m=gI|O}&D`I~a5^`oqkvc7H z`-{Xj8$y`}kU=vp-2G4Bu%vd#g7F0vZebc#2zLeiyb7b2z0QeFy$g};ZvCl6CW2) zA;&zY7NW*n$k0_pzgzyD%JRmcU&S+2jtK7YW~0Keu+*N42pxO|s=@-5kVkiT=7nxB ztpGrl>lGMreM%j4H5uGwPiR)OERo3jf5H!QJYcENSI)#<#$EoVGsH-WKD8*L-CsL6 zoa?n$3c+5>*Z$5qG~Ttc)6ntIs&5jQ`(3*qo5SQDNgsI51)=dZI-b~5n)^rx#e1qE zRQ;+C!)O3}8q(xa(qhWnDbW+h3dfwRzbZnL%By{mU=-`OEf3lJH>RwH`j{4Di%9H6 ztk%1hH_<>1Irjh)@M^K1{L!!~x~@gtk2xerh9 zkGsL8g`<55^p3GuM)pHO-S>3hFwstaVH0t=k!X58H(Y+c)C~z|q_=}J253@o;=wJ9 z77sxG>iX&=yYvOu95^69)n^`K5SH@_1)6ivJ;hf#znYKB=kBJNKujwyhm5G!l?qwzH6sBAeaHB$ zkV~dg@Yw9_Z*+q2vf^&0d7N_W3?O7RwZT~|3;UyV3Kq*UfBe`MuEPkleAYQaNEq$I zD8TocAXt;Mu$NmN`f)YYug`&ZLpo|;!0 z2&qeDp$N8I!yk-(>QAJF_D^gS!?{HRB>*wdp_V^7;@2UfRY`jSaxC&k1RhAKuTlOb_KI5$5Ok&dmq(ZQa0Gw z`yFeQ^VmIgLd$5ZsGxf6_6~-!Nv;f2%M?84#%h$;T zwaj5cZ9~R>tYh*qe5G?X%odt=z+W-T?5@MI#_!*dNdQZL(%T&Gf_|l|{CaDd4Quzh zq;=x#!|a#5S(9miI}nn+IO*rCn+gNYq1JY=Q(auhRFmVKSj>fXIg+KcKwP{*6LRnrJ|J z4GaFn;>a;fEK(J1hC4fDZj#!vfRqU$i+hmpBApdG>RDu^WBM{1mrqu|y9)B3yATeA zgHT>{@=@c;jQA_6OvwOUkbz?xJ5NaB;!zHBL5 z$1kWVqu#tInz`owuTb%8u`Kovt#nA?i#qO>yx>;5B8y)&sDftj?K;H7pD_gd^mFLv z^NTJ76q|2_X2(R0?Pyynb(4NqL26!pE30{v+V4d#wfxR5_$Uk?CcHU0?26wkl0b7P zMUC94_;u8jOoqFNVj)2v{?cwruY{kq7=j7&!A*0MFiU99sT+$*eZk7P_+PzEIPgN<>{=NzaU4(zaXx^`$bj*Hp3sj3%l6FY;%n~rA2~;@ zP5z3`2E!d;@?^~8H2zU>|L8|XUaP-HH$WHqPwXE=?3)K`E*Z5ouaoijSwlOoPuy7^ z3=eWmvP-o-&1*SlyR16-eo(cCpR#Agr(s0_@fH!5@94Ff*kupSudz7LEq2b)kW3}| zvyVzw(0(1t9~5isRjFt9;3){@@P^>#Yj{}pEouI8FBm@kazB`&|LwyHHVIZLK>l?G zOV)jBduy;nWnH#Q!Yf^7#SJ^nqi$w0OSdc^be2~ONr_JCJ7 zJulJhA;NwLCSfbelS^<7=wrOlLNjf;15*}ismO1%v4!YvHksXyn_+z9* ziYOeJcCQ;kzF~ypC{h$~-vdzKaTVv|g;%u!G?k%N1$)9tIM%hx=XmOE>d;&_^%2J^ zdyBC22T(Yf@6eO1C1;fK4fxV#Q-|-3CJQn5wHS7-t`r{)^Cg0OJ;84>OGsG^-#IU* z1%@jcl1+8iO+Jx1F%7t8sZ-80Ja3sQ`WZEk{N#(^JICz;p&W@qI3tJ49jL-D_PQC2 z0H0x4p}$|_q-d@g( zYRk^G9ouL?Swj(O`Mh`1YpEVjZ##CC`)n6@?r0EPihC_qgZoy{vskdQCT{3NcE-2| zm+?prx-W~)ql^0KKj|RMk4E4+s9Yjp*&yw$ zGpz@6F`s#CZrG)T^bFIB0S=Gc1Z+_67c)|`s~aH10`4-c7dMcP1w@e zGmhbo8M$U5$2RC26Zr22o2{BOT=Yu+SI1Z?8NIqNz&M2MBPzFl82#E+O;=en(47(W z^Lm+vq~jSmq$_iy=JdHu{p1}D6fJoxpK>tJjO$PugMCeidn=M~=F*;*j%b`Dhr+yk z`_K}^gXxX_Vt7lRTz14PhD^5lCLk%}8T!!4&R^aSst@f+=7Eg1aFZJ!nBKx-p_5*QB>%WWu-K&GM2oNa0S@Nv zZZJF^vlf@f?1;!-!oy3i@AR4)LJ*>&Pb@37buHC%o}^hp50bTMaU%XQ6;T8XE0#Ol zA=f6a5%%|-s_F3irdGLUE$ObuMGfa882v;)jK-Ryh?`I< z5eF2`7H%%4Mg1Mh61qk7C)lgg?BCTuhAuuewaBrM9{aH}f|xz8#39edISR|VWiG^N zFC<;_jqA%pQ+KkcJ;YjB_b)_oqFoHz*HsZr`lKGh@W~4%bLv_JU=g-T@r z@C2GNQd;?R%4ve^XZCaW$2r1YzZdqQ%f5n5&Zd;M(|fHiG|b0rnRBHM$L}NZ#h1WP zrCf4djL)-n?<4-g6pgI;{$kqPaRV3<7ULBWdhbvzb)=pMP@-zWRI~2+rfOCC<;g`W z7m)qmnojKaoe~cG2-uv9Qvq6s6!ENOAU*u@m;>fa}%<$ESU6+q5k=dlyDddO3KJ#rkVe1^4sm`#O%Js9eK4t1vKy9mP}|2_Fwf5=!? z)T9N8NL(|w{}|DVHnf5nK#^A&lV~r^PvIy$Jvc)Qoa1fSK~T8)roHtTRP38?j0Zv8Rl)R}NBDcF+KlkC+IT=KS@w#dEhz!7 zjL+M}3~6615eD#X={$d%m?R^WRt%pF0a3uHCM)PMraqTx*$z}7UWX05O>38hLArAu zB7tEH5fbdvhX4T)oeY>6svOG@tc;H!PUE{+DcQ36FwPj`USm*juIPRz5c7C@#kl?0TwYHp)|E zdY%jhq6i_B1~^+YJ`+WmN`n6Z;?g5BXWtsg^IEJ#+4iiq9=CH&jG5U_|Np#wYn?`d z981D?sn{x~`WzY7KGRZp2XG)6dcRvWuXZV0%<#Y{$44nTxe>JjAg}$MwIhuns^q4Z!1{^n)JQaSq@9kg&dgG#VI#>LQ;w(AZ?NsAtVGS)TKofAh2p0%e z%1@NcAJurG?%ow$3?5d)D&TN;$%<63_6E*Hj~U;X~KT0V2 z8!26z&E>@KhtXC{(@pkW`O(oDsdJr~%~tm2GJQ+c_84tW`!2^5lxY7eork{~Z|z7h zun#oA{2qBaedpU38S9=BO8!l(r~JzcXyJDk>@o>dVUf7KTT+yTo7lW#-?ZBC{gY#v ztw;Nqw4g#pn^x!8f7{4dzSQ$mAZad_kF4pN2vB_zG#Ndy4In%Vy!>yMsiGb#PU2lj zGBtEHTquXLt{3~`5H=Iwe9p!OsbZT`Kk?6~;6Lyrt>YLZL{4I5NqrAk@4}szQl;G91Kxmtxplll+%#Or7$qC?^TvmS1U$kATgvYCY@V$N?Yx@FTiAZF57E<{S zQD+0!i|x)RN33L~k6X+K_vhi*kT9!R@MQP{$IkgGE@B!3Tp?FhyQ?`}_PSn8_m6H_ z|2Fs_O+v%;<^rx)ZJvCHi;R(UYfaoMgflZ&e^zBed2Jw>zjCqFkqO4Sh88eKG_UG; z@T#<1O%nBHN!x_!W`j=pJ9cw@=sHL+=3V~JvEdey9v&At!5%ji*TqC9!L0CB=ulEhe8(IyRIxoD*$2n& z)K$gE^Ue&*3l1dZ<2CxTlMQo%7!|{cGB{Wgp&eKByTC@js}yz3LXj5t_?~bIU`{Z( z%6(CTaUhJy05Qe|Q~7r?zA<%rTy;w07V(MLK6;VSc-ag>hXqb^3~i$0G!=^dq=mIe z43uwI-CYFYdm**%EdCerX2+{kTse0iWQq%DrpRR3J`1&THQ9Ws4}~UR*G`qxx+mm- zf?{}zGai9HPCs5poBd-CH>?OzymnXtCkfS(6rw9UbJ{htTR;%;D!+Qy9(yxB;fDhXHBClbpSm-nQ2TF))1*~wH? zH^A}kA0HE(m1a91L?*SCUUEHhG3XW6e<3aZv0|Ec9cxISIb1vIAKGG%cN zT^wS~pzKrP)@dAqwl+hu8b|xRRy$eG1Jlsqdm;9^7wb0z|635vjH|anfNg#>>D;Hb zEyH&7Bs|#G+--sIOMUh;orQ&#omQE{p0U3;Ti0sglRoP}G8kq=e8(~h@P2Oft=~wI znR_=VF#WHPMZ!!-LQY>K91V_*!6l~O`Pe$EOd{(< zZPLC^bEBCiYqihxiX6WG)MCZc`)Ge3zs_R5V+zq`7 z>SX%~e8RyC03?}w_jkLXeyy3~TyZH=^0p3MqVSsO>&~}0hc0$!+2vPpuehJ+W}aZw z820yzghV*>&^s1_PY6Klb!!zG@&0AD#U_uGH|w*~+N$wy8`f2fuQsm%Rr>2eB zzcM1V3Pl@2Ryka{2Q4dWrh!w+aW3{@rDjdfkMW+!yT*MT0FHjdam2j^=Hrv6g1K%M zm9F7XLEv#{u+nOY&$=077iKdj@lm;wn$?SgCO5qw5f#ygPP3SHl2ICYsHM|htH8gwIv5|Xw`3Tf(T@Zv|97s{S zaEo2S{+La4W&CbUixP7``pqb1dkVNx-6^vTUSWH~LFP|t4<61f*ur+$^;rV~qVs;W zC%RDs@CX6;03Z0ioLHS-TZ|dAvvh8Cs1_Vrg0l27E`4Ywc*su~Gj`6UEV$jPG20Ng znH;ssR!#lIaJ+p&1%vd`LlVf_ApEF*;5M_!sJh!C%%ke z2{u-X^!RFs(trI=emBbikrV^Dr5<$}9<+WmH8ZLjf_g@#v3P@3)OAnZvuJwQcg#ib z7Jr(%3Hu{*DcLZ@Gqw~a9Ki9ofk%_)Srwu{{P{PWEtD&+54?Y;;Eqr-5u~J$kboGF zAjzup5S-mAU~(5%$m_O3jkN7$kwGOKTH+bX>^3^llC6R)S!swMAl4CLFRH@walqoA zI%%P63bjo>?dGQe+B2S|wH2dPNM-g9{pU1c9$906)&}kNGII19!s>WJnF4o-F_O?(u7e`GsA`^^EziNJ;EMeC?eF@Y$2G(9t}D?IHonE@u5o6rnj_Hk{!>=YcCH}gdC+PwOdXQ*qN>RWt1L*7EuMu@Q z#71iSW5JB8lq|%*G9T6{@j8bHWl~nzC%7ofgLd(0fVoliL1d73{!3xx#&dGQZeW;V zKaiwLqiK*o(xewgehH;g3bY4L4i2hfudv+0;;1Fn#!^^8*p-qsXK>%f4F|?w=odVj zgn-oWLaO5YP`WZ5;!Q11ReDGq028@W2SdwcVh6AdbkSQ^{w(l|Pl^=a)pIMFa}f~Q z2yJffm=mn<)h(-IHWq}-wc&n+&#Af(VpJU%Jc8%aj-{R11-ne&$0K;8QNW#<7G@(f zlK?NV^4ReuVRAz+*!>wF%O-0B zX4%&N=TQX6R@a;w`mrj}WA`15gdUSF?Mj))#5z$VhYC^v_}Y(~vZl+4--t^Lv_@4LNxn#epJNHTkNm#7}5E z2=j3^2a?!?y>Sf&T-s%ZDW590HR};u@!!>Ml~; zYL!GHP{m^KW1;SK3*uG1U( zcI3WT5MEs6ji`>lkA#_1?0|P}CZ6svUb17!02$m?WMHa*!kRcn593SaG8fEhgDJ0{ zqx+({IZaw58Z_Em%?GW%&wqYC@7~%sQZ#GK;Bm|+$?q4bSU53g&f~FN*bwMcJZq?N zbSH|Dc_#$nJVASo={fPxp#Qyf+GU7a^^T;+4e-9gdk;(lg(`7zNfy=8*t^JL&hC<= z=Y%=Pa2=W}S(*iSJmE)!SNM=Io?P_+{vite`$${GAbK)BtKBpZ69byoM7SHb#vPgz zJh(5qsApP|d`P@Fc{v4k=y}|i`yin~e2P*`hkONnu2)C-T%DcJt?%qGkD{XZ!hpXF z>(NY4oZTdGI)cqq151mqkoj*8L_0$#2g(4?lKE}7s}wxUUHIc@W2(6nVg51%AO&sD zMjA~)0Y9pyxF?&oKRlUU7HdzK+^??1uh#PV+hVwsqg&b5m0 z>c~(}6O!v@#8R>tvq?TI*E$O}u;-E0xYQ5d5gNL_R2R0RBygknh|`h%r5~O3mviy{ zotMIZ4ZwM#2a0CyslHRqAw~kZ{|kYNXhvy*@5~Lf2}`po2}=}x9i~{!Gg|{r`bGVg z-|A-L{zwx>DwcmAv?qTWY!4lIRJkC$Mrn4#CyLgX$%M1Lg(hku_cz5Gd>$-}gKuaN zhy1p2dM9;WE&1TGtP4kg>C0F}#0j7Q^;)xKZ;MQBs~fOW8cbwdL6V@0oC+dHf_onR zSQ%t|nhkF4-(0qq6|ihfF*S*iP1Oy~q_&a6zfr4fFnF>-4x-JL zGnv^j<~xmGs&U<}32F5$hH0F_^ZSE6Mu0kOsN+E5n^Hy73tbu+j&U`G1qWwkguJip zny8@0Q;7am$F_0ykV5c7Pj9yhOeDP3r;&|#1j5+Lta?m}{w0M?+YhfTCH!0BU4*#h zp%$6Pq~hmWl!u-91cKE9zktFrNKOrDuiLe3;)<^}az9w6(!+&QN$;O4iZo9NXIy|X zEev2MQj_|bzKR(8iRl+Au9XLv{}4neRDbjjNcVHd{rIUVLLvq50EEEHq?VV(UYnoh zVDH~eEsd&`Xaa4x5K4IpyV2DKj%I4*ab}MOj)qd~-&QX)ema19wwvl`x#m_AGnPS- zBU7Q89<5)-g2alxTqJH>ZI}i0Bq`sJM9v4#uc9LrL34H&2wXu>&h?QzmIYKWin@$S zqEOmM-CC!eWwODJ-1Ps;aP#e$zMv-RCO<8Bgj&wLQOl$tt%6L**Bll?i{Rm#Fic!tvTLDXET!n@pe=G~*tVua zk&9(7(&+)4S08MA>&%&8ZNWV8I-6D5ia++L%pAr{S|edbcIF;{xmp0OX zY0rD)qZ=06=!~aMs-2<7hv(Ixx6iRcKs1bY{Sz%{dqzxvyGnQ+fu8be54VYF>8ZyG zX0qE2;FnSygCEq|1@!mENSJd+@s9XBls)a>77cExlY3+^ z!4_qQsRdSzwD+SIh-I3B){2s*Xb_ocPKkSf5@GL)r^ahjaC|H9!Ju4DcLi!+d8@2c zAiUFsYINrVitdRDup%p7n>f+vYWt%prI~I&M>Zj>A?R_fH8Ls8#ZCEZV|M&AJVc^$ z1)J(T^XB_8&$UKVSfk6&dKE}4Bx3ev_~JWh1-OfqQ82B?Uze94M~$AiB`i?Dx0kcq z=xJI}N_4To_Me^Qw2Ck>{#2PG4z#$S#Yebo7*7iH>clQ6{1MHM?9?UgpgtwNP=$>4 zQ<6~Knl7Le#xFt9EG;$px>j`FKK~nm%mF`SBWZC*$U0vOGhke@4}FfDrqtvpsp5>5 z!SRgOWTkHqnn%h*ngGN7u;oU%^7gKy*Hz?L1+$noHp?@fG;CIV$RWZ7YbV<7n9kQt zpFBG>Kv_HhWUiMvB=ywW2C~)~VKLZ*)KEn7r!;+}9->p=o;>o%ohS-I}0|Mlzkmlk1IC-A=HE$|Au9(OZ1UHjXIGAK%Xs zGS9+#L>6Sn%vs2cAv;4e!V#Z%1hpdntNLm4_x(ap(z2~0_Ch88;zQSmYuI4R+f33B zeFegxGEPOHh1sP!@!K`UoYAR9dgQ4V)^q$$3U&plPfMjeS@qf!ALFuQV5y$5fY#vT1 zWnJ00{gQ>kurMt7UYS(E5{Jd`u{Y-oCOXO`ZQA-r`>e%fR6~NP~toc;9r#6750?Q zMuDEm9eqAm{E{1GkJwYydItx$iuT=k$n@eFo1C)};H5zh^)0~K=$&cqE<)%rmXaqk zUj+zZqV%GEPqco1zLH&XHr?#ySFJG(0s&Im8fjIRu}K$>qrcd4w03HBD!%ljH>=rk zw$0SDir}}-1gaAyq^%0Ze%U|uyV3t?q4~~UOxx4F0o=1de4z{N&K2$#o9^L+cCXGk zw8;!YUBw5pI}QGx`GP+W;;KeffX^s%oyR)FC=#@&%a33@ccKTKp)|5T3+JO?syM%0 z(u+EITJ)OYv2wx8%cuCgMSvBzrwq1b$mw}3;uudsd8D4kDrpib`9?<}QI}&bvkIWu zRK@k{VH&DTJU1FOi7)0IF)_c_o*Cp(EU>Kz*OE4r6Q4v*6k}i-mX%0v(O(4!l{S`O z)PJ)W$qK~UdE@tRb$uRmoCy2^&8N-^b2(&GnA>GjOaEm8~n-$31^y@+W^b0}|j3+pM$s*e>= zx^G->Mn{i{$LSNCX5pPijrz^sVM?EibSLU^1h`wm9vev?@u3HyQPBl+fzh5^OQZXBUI9(x&bwYj+UT|y%UQHmyA&Sko0$&M|C&ZmO;1Mq6 zcf}?sDggbjPs z+yE5R0UeH9^Mr&9RK-;Z%Nwjba;i-OjzsVWn+d_nYM_VMM~0&&q<%Cx2`KvQ#o#YR z>jmIi#*SemJ;yFL8r<8vi4h`T=XFBP=z+=rz zOm`)*IM}_$5?h@{v73oD_qQ2~;#(KK1R2~P+Okcfl&0ztZl8S2iYRX7vYo5{2BmZ_ z;@A;QjNIuKI%`}iB0tp!Hlu^!x;kmuu0QmIU_(doia5}JZt zW@fHTBO8>|p!Kv)Z!8#hQGypE4>fiCQevtJ-}dV%*oJf|Cr5@Os!lt{#Z^)<O@{j@rCu}Felr$T7+w0-D)EuV5 zok`iv9u3k?0ZlPt)vZQIkcn2mYi6og*ZP@)KdzA283v8UL-@Y&UF3CZ`87d?_aKp6 zyD9=hVIaWxI}WXHQ)>i@tXjr|>}a6OIsmS2q|5&HgiFRf^(M2*C6?wD1g!swtZf9+i>a<}Y=0w%lDCc0W`RD+rkLiU?O%SP z4&Ydjfp(`Ss@-WW)F&aMf)`i+n<)MPkYX=B(Tf-}+tKZ!A$Y-Ap2UB;C1oA74=gBe z31s>(UmEgObS*dk#qvBEprOuJ@0000ZG?bVLNcLj+Yz6)(39)T))-hMPQTp^7F4{y zb2Ou+2!!6sSSh0%oAx%stp z5AriS3A}w?2cgc`Hp`Ye$4(M&d)cE;q$STB10Pb8_=?*Gk*J&`iG!BPqp82G93{7N zMbnbw)$3#(={PzTxkZ0d8Gc}=r$x45!K<9Z+3`iy_LG%;Q`zCL8t510u^Vn|RNT`G z`}2FkvdoR(DSn~XaZ^4jxFaE~5lq$Z8dPqjdRQ)InIQNVG+_ zX1bP8=6!@4tdfUXm9QuAz(ziUEowz{*O}Q z^@&t@*}un=hwKw+NKk+m4m)T@>ieZogy_Z`(HP^nYwW1l%Wg|dDa{OA;Va+t;D3KA7w`*8eh5YlbO+I+t1-8$r%!y>R1o1Yk!u$m(WqS&(Gi;Ec4r3CiDpWs1l z-FQBv@J=ol2#DR9?OMkX&TpW?$yViLH!b+&@dvRcBiif%Q*)RDf$Isb<dQ~2Z^aixPygMD4j=SUeaij1lPUCRtRZwhDI!I($@CwR9 zR~tva5Hp%X+=ZaSWS`e}3H;XIc$S}*K1xsGHKrcXUC8U!mZm~2qW@$`0KG8j30Oj8 z{tEx?Q&N8z=Ej?LoLp0%otn)5$KV zXVL`>F{i)qJJ|zHoR~({SN`Z|RdZ*@QD5x3jtR9@i+ElslzHBmdPUe(F`M>WmQw-M z6mFgMlU07`Ka)2ry>6^>kTI56RnQ)UyNe4-C1wYmSit``xwthYS8HsN=cXaX+-Xo^ zjNg$W#sx|ztyjfp(0@3NnzB3Mnt4e1BaHQ&JHUF{Li)VU-rbddPZz3BqVrH@vg7 z?2bn!W(Eavj}7CNY+z&~RY}QI_5lC|5EKo8b=D?{#z}rghj95d*nb}}Gk8GX;PN}t zk8|R0h()%N3wsA@OQDG1sUF)qD}~ zE_YSH(FBLoL7}Z=tc*E6onH}Bm||bZmd)3MZvFR3o$U zB$mbmV0MTwFEB?}SU8OG7+_`oY)NAyi9{*N8s6o;@Dl^V=#P)RyV)MI;yuAQ18ag@ z%i9J9s{`X{W9<+C08xaXHN0+xixgl7*XZV@;W6tP#Z4b_b?mDFqW_9NVhMLsCQ93* zi0tFRItGfqDAPQ&;1jY>)3brv_B94jBUJ$URD#igVKwf`S|V{qj$1nP3OapJ?^V=- z<%UD5ZI#A9QBT_XjX5bLIklo*4A6Q%Qs4bq_v86EsqB@hj{0oy88(_?V-& z8B`5AnGWO2TMPsgu4)ayuF4)-P4(PqZ8r=kxpOv~_Kq=iv3mwmW@^QMF}7{E=v1t5 zRh<-i8<{f$Qso7D-lJl$A#rRwhK-g$)NGxPlMk74F0_f$|xzGe^!v6xh3~$(i?ZI<$(Bgyz zw-irzc_KNTb2URmf+&RnC200bm`(Mi%2{ZFa>j7zCZmu6#S7bPVW>DBM2=21=8+*B zpd)yiUz+vU>~8@hPn@i=|Gj%_D5=c@hlC2a0bX6*sxus zCd8zB>Ve*9Re-Awv@f1|Z|Svy(IvLkTeZOYF2Waw@3t-w)d_Nn*c>C8y(&lDQy(v@ zsnfxSY5Hux&jb&_FO}M+31_RmmCT9F-!sEJhc&hie_EF_6^vQ}mvS_uBo|35;Gn5> zck-4a_Ah03yU+bIZRL?BY(}wNu7U}&_oFZTL@1|b$aje3m9A+CK8uK+IPv}Bk)OaQ zTo|?>x~HRQlQ+CcxP)>hpIL4=Vo3IVk@`Irmm8pLLs#cuiX_Ra^U)^-(cMnfKVE>* zpcDzSZ~`TtvR^F(Qd@LK+qKJ4X(}Rh{k+0cHhTENjRvlS3BgZCP(?zk+vWd)(vZW3 z5)Awfj^f_@gcGGpX*`1>l{piW%-x|xmZD0W3@{0lns8d+fF&+R)h6LQlJ6xWw)v>p z$aNQOnwJ(YYD62X$Gb!toCtfjxfyuEn?p!7qr;R+KPM|JQwdhNQ0|KUk_9n;6LI{l z-GS69W@Y)}^=z-qH#LdHeiap@Il#CUm5q+E4+vQ}lAOVl{;w#j%X#`4*)NK>?IK)7 z_>P}DxP~E$4T~IOC&Txa{brSe7ZkMLAJ1#Ksz|WOWK^lyOGSrc-w=|nRB*4l-|;{y zrmY#2HM>o&7$Y_ZO{MTUqQKS4yC-@dv#}XoCRTA8Y;j7 zFUnkqVoY9}$G1%HCv zEGy^`iC8Gqzo1fbk^jrtePc@@x-5@H3Y7JT%tzg)?Ha3)h|amR02K@8ihAwmQYlCy6r}s_+XZBSdiwOwlnYC)6E&r!-MVd z(p~VkTrsH*s;73<*$dI4nFHDCh>gm6d)I93a&fv_96+duOd zPWwh5-M}SP1TL;MPg%AZoimxRlaFL`kEPpa{0MzdB-O7XU}>P@$UpGJfm*l(s6dmQ z0M@eU48O_UaTX}H5kW}&#F74ax@f_xI)&A;eAmE1#UW|!`MGGohDP;yh!Kzh$+i|f z$(h8lZ=?B8W?g*=GyhnRIvu7Fnis8WB3RAc!Ud!w< zIf6ubPPX+|Q}q2BX}Flr1RaJX zOCfBuw9A=tATX7VKTs@oIS@tTAmbX}3+;Bt9k~3yug6~fy zf}1GGw|}4#gyKm*4g(WKH}5){{6NLKLjrB3G zGY?h22H}KwpHi`**n~Q>>GTEPE|z^B)ayeV)SDAtsESLks3~dmJhO75$QPOAUV2=- z6kAge{Ab2q5kXL=09`xSHZZZ8I3z|)$9wQ8|01@1ib#NpjmPp~e&HYvji@(BNN|Yh z+ILVd4EZzJL#r!Qql+sAd-~!Au*$Jf(vA8N?wdA&JpMMXO%@8ah6%l-5Fm(H^f%gM z1+zHeo@C>R42Ieu`5q48+;JveVhmYDz^N*pA)cS`W-KD@Kv# z#*`BLLIz{Qfo>J^5<()vNB%b?zQ$<&i*2803M@(F%VMHijh;<9+S~NP^NCr~P_{SC zRyKw8N=%9){j#@`UsIUBi3!^2Ni4R0@9m)Wow1^|KRPSb-co^ZC7 z^?=zQrg=@Zk>Iz;4`k6@yvm#vGRqJDO~I8zq=s#kr)D;m;@lI;y%9qH|NhktG?gke zn1V0PMHB$wDR`n(t9&JpD8Q!jn;)*=T)skz8}Gr9)akc2lsEuc_GPxt;;z3YQdzQ? zahy?)a|+#Yc2|*CKzzF*KjR-dp*E6w>J$vfPvkJ~Qxd?S!xL0bHzwmw$LcR1&hb>K z>fLPmWj!VRdV*RZ)vI)e{!kcze~x0nDgJUzYH4SfXR<@?NqJLZT1--CDQhtV444_l zyClewclU#D7OXFkYUIUi4%mgs*2Sp6PH(p4d>aaBuRA)o;Cl_68(wyzrBEaS6k)9X z=%i+H9*@N~62Njkgn~ZtVSt38^*nzU7|t#2D@x&nenR&Fab*aHYCY#ff_nbixSDY; zQmazkH~ZKyR8i*J+9#SFY8%9V{oS%|bl6RT7y27Ir6Yw|hqh@}PpaXw z360uA4bA~nHkixg0Z`N_mXtf}~tl~!o%AMMKQ%TwT=g>t`fN-&_49HGVuO3m`b6Yj$ zYfg>eBv)a37BRflnlfUuvvH{$ukLDz3BmQWko|!qEu!XWtVWrQi=d!5G$r!~PE_AQ zQ*Q3|g8OuHPM`?%Tl~DWKl4_7ekr+A;l$+|Mx5mWW8e!;Trkf-PR(^Q|-BNs!mzqrqm%+*Ahzu=a@n^uFB1 zFTSiqwB{qrcvie-)_wDJGPqG4r_pr(wBsVc9}C^9o%RLTh}xD@dy63v(3PJ|ajA3s zTud;;L=OK%j%QkEXYHYvyq5}mPbDJXs{)~yE9A{l#^)zU`D7Wz>iyH*@D4omiCjKs!; ze%m8H0p`3E)kwNjxNKk~DLtCsVsuW!<<+upudDAxfYbdbvOl`b&^nynapw-cB1n+; z=cDTUd0<}~9%XInLLzkyXWUF%9&GsHw4^a5b|K+dyY^-Om^1A1!gNqdB;Xz;FZ zQk^htv3qA9*o5(UrsJiQrHHs`)_{?ASLPNUSgMZ7s0bMnvx+@~`5d1V*xtpOH%y;# zaA<&A6i4y46_uv2*CZ>t8_YK4>w>|Hx;xg6+45Qne_3xg(E@3wv-#J_<)*C?b0sq$ zC<|;k@Vp_q-JFCol@f{80QQ%F@Mh-jgB>M8DqZ1!6V=`4!mOsQwF#vFiOq}!j?FGD zV=Nx9zJsNdT~Q#$Zbb5-wiqH^Ea5mV>(@9Jpua(}jrc+|vBZND*~y?D#OewaQ|)NI z!T)!Ix*wHfUGX|~kHj~#%|F-XazB^aqx}Ve7``5rFc8Bs3TSz1ZGJapFQ~=wT7CLG zQLHb9vOYf+K*G$`h%+}5&v-Q57EQO*2ywCGSnO|k0>b>jlnRIns52^faNDaX;rtE1 zy%5h_!-qKX1Z({0RpivYM_{qvYS^lhQ|7_WqM2e6RcsQXUB83Y(29A8XxI9^#lFrK z2NeP(Y$5;EM42{h;kVTr`G18b!bmJZ=nxW_1L3V9EpGn2kGkD#Awyeqg$P9`fcilV zzB=t{tQEWA*Q=s2Ewq&%0AxOT#Y&_)n{+a!Voo8`*DUuMa59|Hb^G!a-5<;czW%rAU|(0IQ3!`vuTs(r$WRGQ!ZQ4<>Z|M&vwV$f)6$>+Dwc??6}a*q>f7Js1*JG}#9 zo8n25WhzFnpX$og7hh0hsWG~3*nga%pkcvYUQ0dXRTLZUJ~kqr0Z`)u*Vj!n3&prB z&i5fd%4?z#S@_?F;uJKkehe5nc?X#2Y996;1!62G%vEB>S7xyV^Bdr2()GZ#H`MgM z82eUGwZG59F2ZABQyJGMRFeOA1M^Eh$FQc>dSRdbc8&s*99Lo=C^4Q>@+?;4&wU?$ zqg3bSEsAkSlulMmwbU5QAg3h#^SoRV^?pwB2KlY>dlHui6kCx|4MNjL*SW0X)mH@c zHQY9&Ug(vr`*^YpGu*0EOU~C~_z@Km^Pp$KZFdri9Kb9jsAN2srTPMLwJyEdk0D#Bt19-gJ za<=`hIn3kNdTZaA2OaGmD&ZwE*t&-gEF4TMACMCGf3kbswR_sjsZ{|*{RFhTjTXhN>HXwzr$Co zq);gVdi~sa^OLUM((7wr{UHwZLrW3}21?GMMS@fkV-j1CdekFH-7y1=aGIDe zLg_I~!FvG;QaB_2lwE_U85qfu69*W>w5X|wWT!BU8UX$!)f}id&ZS;pMQv2ba+5w% zNXr`NCzDzp*E2`CR?GhYkvM${<5O41jN`;&WVuxbw#Wh(m!e~H2-eVF(--fUZOF!J zCgq%>4V8Zffr~xjn*eTM?UAa-0BIA;03e+U263l>hJ8!hC&w5_3fe!Su&(d8ett8S z(cj?GAxJ&*>!`>F;AIU?UK;KeAt#UK#3}|y-T!@#tO8*OHJyApdi^Tr}wpfYye^u1X->GEhhdQAvVeaNqjO`N|NEsZ}Fketz<{L`pX%H*A<2 z=i1bZ;{CWQXzhtF9dY2K&sU=Tq4FVxd(TqI>U}q$s={;pH}55!@q00i-IxopMHq>p{er-HPf?QV?vtKhrs|4qAv@?Is|<3N#&O; zy8`OK&hbvQ;ZeXr&n+ddLk}&Zd;U9~IML39PRTkZe?-P@^|?hQJQck%6{``rc(LF7 z{&tx?>2&t%B~cpPibwDLXvwJ(lM7Q6h?)6jZ`a3~G2`InY}D;23)%{&zuT|C*0<$&cN0_w-e73NqLI~iuM6;$MpeKEj^ zC8CHY0WctSX{zphEKhx`(a~xjMgmBfZAxV&kp=%3%^{p+&&M7^4web>E8yBqJXQFj z%(Eh>gqSX_q94br#fMUfkW9u;$sew?T`&BpBh2iKMt18Un8Jo}&P|P1>sMQxpco9d zPfzeWNY4@IoQ8}C=t@&|*VYPKEm9?l2b*x4Ac;cg#-j-8VDnzE5>3owGD@Ujr{LBR z;M_c-e|eBd*vr#t6v2mdr=RYzMxBcpQnDwVjA-k;kGK%C*ANJICZoiH4RKg6A4WU# z=^yL=ZI72iPr!jP{_l2etdYqXa9zKSdAhJ|TiTZdy=qKc!1;TgK54&1tJNHt zd6vx*MSH?h^_JW4#Att0I2D$+_CSBtI%K;t-&LdtOSRe{XQoobqr5K8k@T^=vW z0}heTF`Bg}QB&L-O|9KS)X;48g2wjPwEL!ufn`5VN;q|eW16(n9il6YbpeKQRMPF- z=8&l!F>vXzs2ryReX5bRA6*ir=FEGri``ap{u=hu3;2XjmOQ7n?f}+I&WpV&Uv`gyHU)N8eZA$>JT16JTLoj!%Rz|xHcG+aDo|cvN zcP(0?$%&{A1$C8~$gd`L&dqe&xyBSJ#RKLisDB9EtT?q3((y$1w(oZJr%=e`R2s}7 z*VjY_Bz=zits{Uj2R;IbRjTHN~dAO^|gR*s3MVy|~YH$x%O>9kEJ>o4J1oDEENh9KL z`&VuaWMb)Jg#a=35nY%$wObQh0vkDoN6v2ExE0}~VYNQ?uz>?RS`jIt>%JYd} zY(b^jD_@e^h31Mrd7FxD|F6nu*TonVDJA${Hwn+MyXm11r*TD>N{5 z=z(IF7Qf0tK_kkNhRqYFiM&W2a9o}X;{w{Cdi6?tFdyRj2yJ1S3VAZW#<_|yYm*=| z9fe%dvftW1qzbfEG3ogii@mfD><}j|@!!quilrp$L^WOi#EzoHMht0f&v%E=6Do*~ z`uPghm_;<@rWl`rKAtCd%WC;r=Es2t4d#&<6#uiDh#i$;3DOSUdR{)HZnn`@;IeWG z8?YTvg}^wo>p5S?KCUY@EdF#_l@+3S{sf<=-tF%o#)Mb>*s?c9e^gRxQA4BST$Bme zW?()z{9(z4$~Ak+e3lxoD%HCz%^2!Yda-#2GSx44?P@?JPH(*f7_GFfL7res zZn9ddj%k+mT*`?Da%kr49p(WaYAk%W!GF$U8V*Pg-_vXMFa=b~$H3^Bl1J<+EsESbI z@1^7dU;_Z;4?uz7-sbroa?c#ZHBCm#>srN^_hgQNU}7e<7O=`hM??|Jr=p}?UnB!9 zvZIx|$)OBis9G?JTmm{T_4%v44wVC{K^ebjqrTKo;Al+wIhB8+KAK{62h!ZVO`@+ux8+(*;gW)?y3Q zFF6!^Q(^GS?MJOk9zP)Zd?1C6{g5=8isyeU`t6BZAB#%Yw!_DbADSm}NgdUB9xBT2 z{C+&q&U%gpGc`TAaj0$0#TL2w5C-!K32cg8+F=)(1df?_PqX+W8V*ox{GKufL@7RS z>(U=XQAHZKya{Kp9)v<$&3T|~4-zyXuNn=M0g?r6`nS{W3+fa$34EJK%x=d$3ujb}PiB+xkQz4=yEz8&g_= z1zy}YfTjz9DB4og9}9!zQJeORuRhrBhc+tCf9t|nQEQ7@$bTZRWFD|<8K4AtlB)6P z%oCR6Of?yJ=yAiT$dDSJnyt@b*ihgonRXLIh-?;U4i*)sp{`mB0;Zys3R!^ai}GaL zs+H)dK{#W`Nn5)0+r6VSVC&;=YKl@LBPkqnvSXN2czY>PEbIUvUIFs+Is|;Bm?yHE zx4U?n+>+&a%DnhyoO8F5+$BWOtuwmI&iB&|{}L_LP;9@YNC(GH;Y=4C=Ok5#4-OF9 zB>chF2#UC5AUiWzf>sywuFI)JVHV&z#>mdU>rp>!DYtxE3y!X}9IsRm6h}2t^vftV z!CQgD3)zJ{oCT#CZgvikl^d@(s4$4l&?|D;eS5RjsG;YUgTGArc92DY3+lXaJi*L7Evg=4)KrZHgK zbG_%3mN=zh%uB-0Ni(DNTeETV76UFae;m+Pvc+XDN#77`thSB{p{RMs1ix5u=6xC_ z1mF1E>o*)>WmTC|11|L_r?Nu?^(9Aknhmuzc3r+lHzQ%9hM-o_*-p*Pbn{|UE4_s_ zK>oqUcpp7kNcNZ&B2NfHOvphLgZJmwu@|Z86+=&Pash!qGUcwlGtQX16}T%#)I)kDCht8t!nMB@4bJ=yR#n#SZK=4}DL38mkz;3j3(2otmBnD4TuGbbq^Sm#>P>e}`f zE`|$GK^RFAVaY}Syn2qB(})?xYosw0qV{(I_);O{{UHpXM4?;YRzfrvR+11sdTyX9 zL9^mj8IF(IYd$_rtgNvjlYPcmq*XGq$E4|$f^nSHkvwF_e*VmkKlz;=>+fa+f$X=U zi%@c>HhuyuhB^86yJofi&SV-uP#?33ztqA5y|P;%O`86*K67P(RQFbpe%t7K0x30J zrdC2q`sRu&n)#sOB0>B&W3q3^gKY5E3NN(TE~J36aJ9!Z0DYLTjRj)LL3vx-X^6kK z*aHgnSk>*^k+K={Ebst+u;s{?+~;M%RjhNmJN-40#Q7g@_Bg!#y%E!@apd@(WQ1z* z2j7a$ow%r?{O{F{feZ=J#jMg=Zj{`4)ySQs*|$nB+tQmBBu{Pi3~ag=2%}6sZq0hO zuz3j0^N(E9D5J>esoT0z)4}nu)=UKgs>vqj9OT&d9%mcct@c#&ZTT_~Kp$d%Q~;9_ G000X3+D<$G literal 31727 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 W`0X-G(;zv&_#Eh8002}G000Lmmo+W` diff --git a/programs/develop/cedit/CEDIT.INI b/programs/develop/cedit/CEDIT.INI index 130b3e0c64..140a3ae268 100644 --- a/programs/develop/cedit/CEDIT.INI +++ b/programs/develop/cedit/CEDIT.INI @@ -1,4 +1,6 @@ -[paths] +[settings] +tab=4 +blink=70 build= run= debug= @@ -70,3 +72,8 @@ KW3 = KW1 = KW2 = KW3 = + +[lang_JSON] +KW1 = +KW2 = true,false,null +KW3 = diff --git a/programs/develop/cedit/SHORTCUT.TXT b/programs/develop/cedit/SHORTCUT.TXT new file mode 100644 index 0000000000..8d8d1ecf0d --- /dev/null +++ b/programs/develop/cedit/SHORTCUT.TXT @@ -0,0 +1,39 @@ +Горячие клавиши: + + ctrl+A выделить всё + ctrl+C копировать + ctrl+V вставить + ctrl+X вырезать + ctrl+L преобразовать в нижний регистр + ctrl+U преобразовать в верхний регистр + ctrl+F показать панель поиска + Esc закрыть панель поиска + F3 найти следующий + shift+F3 найти предыдущий + ctrl+Z отменить + ctrl+Y вернуть + ctrl+G перейти на строку... + ctrl+Del удалить строку + ctrl+D дублировать строку + ctrl+Up переместить строку вверх + ctrl+Down переместить строку вниз + + Tab увеличить отступ для выделенного текста + shift+ + Backspace уменьшить отступ для выделенного текста + + ctrl+F2 установить/снять метку + F2 перейти к метке ниже + shift+F2 перейти к метке выше + + ctrl+S сохранить + ctrl+O открыть + ctrl+N создать новый + ctrl+W закрыть файл + ctrl+Tab переключиться на следующую вкладку + + ctrl+F9 компилировать + F9 выполнить + + перемещение по тексту: + (ctrl+)Home, (ctrl+)End, (ctrl+)PageUp, (ctrl+)PageDown diff --git a/programs/develop/cedit/SRC/CEdit.ob07 b/programs/develop/cedit/SRC/CEdit.ob07 index 2e18bccdc0..e9c7a99383 100644 --- a/programs/develop/cedit/SRC/CEdit.ob07 +++ b/programs/develop/cedit/SRC/CEdit.ob07 @@ -25,13 +25,13 @@ IMPORT G := Graph, T := Text, E := Encodings, CB := Clipboard, Languages, ChangeLog, Scroll, - RW, Ini, box_lib, Icons, Tabs; + RW, Ini, box_lib, Icons, Tabs, Timer; CONST - header = "CEdit (17-jul-2021)"; + header = "CEdit (02-sep-2021)"; ShellFilter = ""; - EditFilter = "SH|ASM|TXT|INC|OB07|C|CPP|H|PAS|PP|LUA|INI"; + EditFilter = "SH|ASM|TXT|INC|OB07|C|CPP|H|PAS|PP|LUA|INI|JSON"; fontWidth = K.fontWidth; fontHeight = K.fontHeight; @@ -48,10 +48,7 @@ CONST btnLower = 27; btnBuild = 28; btnRun = 29; - btnUp = 30; - btnDown = 31; - btnLeft = 32; - btnRight = 33; + btnYes = 40; btnNo = 41; btnFindNext = 60; @@ -63,11 +60,12 @@ CONST btnFile = 70; btnEdit = 71; - btnEncoding = 72; - btnView = 73; - btnSyntax = 74; - btnProgram = 75; - btnTools = 76; + btnMenuSearch = 72; + btnEncoding = 73; + btnView = 74; + btnSyntax = 75; + btnProgram = 76; + btnTools = 77; MainMenuHeight = fontHeight + 7; @@ -91,7 +89,8 @@ CONST menuFileX = searchLeft; menuEditX = menuFileX + 4*fontWidth + 2 + 7; - menuEncodingX = menuEditX + 4*fontWidth + 2 + 7; + menuSearchX = menuEditX + 4*fontWidth + 2 + 7; + menuEncodingX = menuSearchX + 6*fontWidth + 2 + 7; menuViewX = menuEncodingX + 8*fontWidth + 2 + 7; menuSyntaxX = menuViewX + 4*fontWidth + 2 + 7; menuProgramX = menuSyntaxX + 6*fontWidth + 2 + 7; @@ -106,8 +105,9 @@ CONST menuUndo = 6; menuRedo = 7; - menuSearch = 8; - menuGoto = 9; + menuCurLine = 100; + menuIndent = 110; + menuCase = 120; menuNew = 10; menuOpen = 11; @@ -127,9 +127,10 @@ CONST menuC = 31; menuFasm = 32; menuIni = 33; - menuLua = 34; - menuOberon = 35; - menuPascal = 36; + menuJSON = 34; + menuLua = 35; + menuOberon = 36; + menuPascal = 37; menuBuild = 50; menuBuildScript = 51; @@ -147,7 +148,29 @@ CONST menuBoard = 71; menuSysFunc = 72; + menuFind = 80; + menuFindNext = 81; + menuFindPrev = 82; + menuGoto = 83; + menuBookmark = 130; + + menuMoveUp = 101; + menuMoveDown = 102; + menuDuplicate = 103; + menuRemove = 104; + + menuIncInd = 111; + menuDecInd = 112; + + menuUpper = 121; + menuLower = 122; + + menuToggleBookmark = 131; + menuNextBookmark = 132; + menuPrevBookmark = 133; + maxTexts = 32; + scrollDelay = 40; VAR @@ -165,8 +188,8 @@ VAR confirm, notFound, search, searchOpened, modified: BOOLEAN; switch, closing: BOOLEAN; - leftButton, VScroll, HScroll: BOOLEAN; - vScroll, hScroll: Scroll.tScroll; + leftButton: BOOLEAN; + hScroll, vScroll: Scroll.tScroll; LEFT: INTEGER; FindEdit, ReplaceEdit, GotoEdit: box_lib.edit_box; @@ -177,9 +200,11 @@ VAR replaced: INTEGER; - mainTID: INTEGER; + mainTID, delay: INTEGER; - context, menuFile, menuEdit, menuEncoding, menuView, menuSyntax, menuProgram, menuTools: Menu.tMenu; + context, menuFile, menuEdit, menuSearch, menuEncoding, + menuView, menuSyntax, menuProgram, menuTools, + subCurLine, subIndent, subCase, subBookmark: Menu.tMenu; icons: INTEGER; grayIcons: INTEGER; @@ -202,10 +227,29 @@ END WritePos; PROCEDURE EditBox_Focus (edit: box_lib.edit_box): BOOLEAN; - RETURN 1 IN BITS(edit.flags) +VAR + res: BOOLEAN; +BEGIN + res := FALSE; + IF edit # NIL THEN + res := 1 IN BITS(edit.flags) + END + RETURN res END EditBox_Focus; +PROCEDURE resetTimer; +BEGIN + IF EditBox_Focus(FindEdit) OR EditBox_Focus(ReplaceEdit) OR EditBox_Focus(GotoEdit) THEN + T.hideCursor; + Timer.stop + ELSE + T.showCursor; + Timer.reset + END +END resetTimer; + + PROCEDURE EditBox_SetFocus (edit: box_lib.edit_box; value: BOOLEAN); BEGIN IF value THEN @@ -224,7 +268,8 @@ BEGIN EditBox_SetFocus(FindEdit, FALSE); EditBox_SetFocus(ReplaceEdit, FALSE); EditBox_SetFocus(GotoEdit, FALSE); - EditBox_SetFocus(edit, value) + EditBox_SetFocus(edit, value); + resetTimer END SetFocus; @@ -245,6 +290,7 @@ CONST VAR top, left, right, bottom, x, y, width: INTEGER; BEGIN + Timer.stop; width := minWidth + LENGTH(s)*fontWidth; left := (canvas.width - width) DIV 2 + LEFT; top := (canvas.height - height) DIV 2 + TOP; @@ -362,6 +408,16 @@ BEGIN END DrawState; +PROCEDURE DrawScroll (VAR scroll: Scroll.tScroll; left, top, value, maxVal: INTEGER); +BEGIN + scroll.maxVal := maxVal; + scroll.left := left; + scroll.top := top; + Scroll.setValue(scroll, value); + Scroll.paint(scroll) +END DrawScroll; + + PROCEDURE repaint; VAR width, height, scrollX, scrollY, y: INTEGER; @@ -371,6 +427,7 @@ BEGIN K.DeleteButton(btnYes); K.DeleteButton(btnNo); confirm := FALSE; + resetTimer; closing := FALSE END; IF ~search OR (searchText = "") THEN @@ -393,10 +450,8 @@ BEGIN END; T.getScroll(text, scrollX, scrollY); - vScroll.value := scrollY; vScroll.maxVal := text.count - 1; - hScroll.value := scrollX; hScroll.maxVal := text.maxLength; - Scroll.draw(vScroll, LEFT + canvas.width - 1, TOP + scrollWidth - 1); - Scroll.draw(hScroll, LEFT + scrollWidth, TOP + canvas.height - 1); + DrawScroll(vScroll, LEFT + canvas.width - 1, TOP - 1, scrollY, text.count - 1); + DrawScroll(hScroll, LEFT, TOP + canvas.height - 1, scrollX, text.maxLength); G.DrawCanvas(canvas, LEFT, TOP); NotFound; @@ -426,8 +481,8 @@ BEGIN G.SetFont(canvas, font); T.setCanvas(canvas); T.resize(canvas.width, canvas.height); - Scroll.resize(vScroll, vScroll.xSize, canvas.height - scrollWidth*2 + 2); - Scroll.resize(hScroll, canvas.width - scrollWidth*2, hScroll.ySize); + Scroll.resize(vScroll, vScroll.width, canvas.height + 1); + Scroll.resize(hScroll, canvas.width, hScroll.height); END resize; @@ -519,6 +574,9 @@ BEGIN K.BeginDraw; K.CreateWindow(30 + K.GetTickCount() MOD 128, 30 + K.GetTickCount() MOD 128, winWidth, winHeight, K.winColor, 73H, 0, 0, header); IF (text # NIL) & ~K.RolledUp() THEN + IF confirm THEN + resetTimer + END; confirm := FALSE; K.ClientSize(width, height); @@ -528,6 +586,7 @@ BEGIN drawMainMenu(menuFile, menuFileX, btnFile, "file"); drawMainMenu(menuEdit, menuEditX, btnEdit, "edit"); + drawMainMenu(menuSearch, menuSearchX, btnMenuSearch, "search"); drawMainMenu(menuEncoding, menuEncodingX, btnEncoding, "encoding"); drawMainMenu(menuView, menuViewX, btnView, "view"); drawMainMenu(menuSyntax, menuSyntaxX, btnSyntax, "syntax"); @@ -571,19 +630,6 @@ BEGIN toolbarIcons; - K.CreateButton(btnUp, LEFT + canvas.width - 1, TOP - 1, scrollWidth - 1, scrollWidth, K.btnColor, 0X); - K.DrawText69(LEFT + canvas.width - 1 + (scrollWidth - 6) DIV 2, TOP + (scrollWidth - 9) DIV 2, K.btnTextColor, 18X); - K.CreateButton(btnDown, LEFT + canvas.width - 1, TOP + canvas.height - scrollWidth - 1, scrollWidth - 1, scrollWidth, K.btnColor, 0X); - K.DrawText69(LEFT + canvas.width - 1 + (scrollWidth - 6) DIV 2, TOP + canvas.height - scrollWidth + (scrollWidth - 9) DIV 2, K.btnTextColor, 19X); - - K.CreateButton(btnLeft, LEFT, TOP + canvas.height - 1, scrollWidth, scrollWidth - 1, K.btnColor, 0X); - K.DrawText69(LEFT + (scrollWidth - 6) DIV 2, TOP + canvas.height - 1 + (scrollWidth - 9) DIV 2 + 1, K.btnTextColor, 1BX); - K.CreateButton(btnRight, LEFT + canvas.width - scrollWidth - 1, TOP + canvas.height - 1, scrollWidth, scrollWidth - 1, K.btnColor, 0X); - K.DrawText69(LEFT + canvas.width - scrollWidth - 1 + (scrollWidth - 6) DIV 2, TOP + canvas.height - 1 + (scrollWidth - 9) DIV 2 + 1, K.btnTextColor, 1AX); - - K.PutPixel(LEFT + canvas.width + scrollWidth - 2, TOP - 1, K.winColor); - K.PutPixel(LEFT, TOP + canvas.height + scrollWidth - 2, K.winColor); - DrawState(text, width, height); IF search & searchOpened THEN SearchPanel(searchLeft, TOP) @@ -619,8 +665,18 @@ BEGIN END getKBState; +PROCEDURE stopTimer; +BEGIN + T.hideCursor; + Timer.stop; + repaint; + Timer.stop +END stopTimer; + + PROCEDURE OpenFile (VAR FileName: RW.tFileName; filter: ARRAY OF CHAR); BEGIN + stopTimer; OpenDlg.SetFilter(OD, filter); OpenDlg.Show(OD, 500, 400); WHILE OD.status = 2 DO @@ -630,7 +686,8 @@ BEGIN COPY(OD.FilePath, FileName) ELSE FileName := "" - END + END; + resetTimer END OpenFile; @@ -665,6 +722,7 @@ BEGIN |Languages.langC: ext := "c" |Languages.langFasm: ext := "asm" |Languages.langIni: ext := "ini" + |Languages.langJSON: ext := "json" |Languages.langLua: ext := "lua" |Languages.langOberon: ext := "ob07" |Languages.langPascal: ext := "pas" @@ -764,6 +822,7 @@ CONST VAR left, top, right, bottom: INTEGER; BEGIN + stopTimer; draw_window; confirm := TRUE; left := (canvas.width - width) DIV 2 + LEFT; @@ -778,6 +837,13 @@ BEGIN END Confirm; +PROCEDURE Exit; +BEGIN + Timer.kill; + K.Exit +END Exit; + + PROCEDURE closeFile (conf: BOOLEAN; n: INTEGER); VAR i: INTEGER; @@ -796,7 +862,7 @@ BEGIN IF curText >= 0 THEN Switch(texts[curText]) ELSE - K.Exit + Exit END; draw_window END @@ -849,6 +915,7 @@ BEGIN curText := n; Tabs.switch(tabs, n); Switch(texts[n]); + resetTimer; draw_window END SwitchTab; @@ -947,6 +1014,18 @@ VAR shift, ctrl: BOOLEAN; BEGIN getKBState(shift, ctrl); + IF ~hScroll.mouse THEN + Scroll.MouseDown(hScroll, x + LEFT, y + TOP); + T.getScroll(text, scrollX, scrollY); + T.scroll(text, hScroll.value - scrollX, 0); + repaint + END; + IF ~vScroll.mouse THEN + Scroll.MouseDown(vScroll, x + LEFT, y + TOP); + T.getScroll(text, scrollX, scrollY); + T.scroll(text, 0, vScroll.value - scrollY); + repaint + END; IF (0 <= x) & (x < canvas.width) & (0 <= y) & (y < canvas.height) THEN leftButton := TRUE; SetFocus(FindEdit, FALSE); @@ -955,18 +1034,6 @@ BEGIN END; T.mouse(text, x, y); repaint - ELSIF (canvas.width < x) & (x < canvas.width + scrollWidth) & (scrollWidth < y) & (y < canvas.height - scrollWidth) THEN - VScroll := TRUE; - Scroll.mouse(vScroll, x - canvas.width, y - scrollWidth); - T.getScroll(text, scrollX, scrollY); - T.scroll(text, 0, vScroll.value - scrollY); - repaint - ELSIF (scrollWidth < x) & (x < canvas.width - scrollWidth) & (canvas.height < y) & (y < canvas.height + scrollWidth) THEN - HScroll := TRUE; - Scroll.mouse(hScroll, x - scrollWidth, y - canvas.height); - T.getScroll(text, scrollX, scrollY); - T.scroll(text, hScroll.value - scrollX, 0); - repaint END RETURN K.GetTickCount() END click; @@ -975,10 +1042,8 @@ END click; PROCEDURE LeftButtonUp; BEGIN leftButton := FALSE; - VScroll := FALSE; - HScroll := FALSE; - Scroll.MouseUp(vScroll); Scroll.MouseUp(hScroll); + Scroll.MouseUp(vScroll) END LeftButtonUp; @@ -1000,6 +1065,24 @@ BEGIN END Close; +PROCEDURE CloseMenu; +BEGIN + Menu.close(menuFile); + Menu.close(menuEdit); + Menu.close(menuSearch); + Menu.close(menuEncoding); + Menu.close(menuView); + Menu.close(menuSyntax); + Menu.close(menuProgram); + Menu.close(menuTools); + Menu.close(subCurLine); + Menu.close(subIndent); + Menu.close(subCase); + Menu.close(subBookmark); + Menu.close(context); +END CloseMenu; + + PROCEDURE MenuItemClick (menu: Menu.tMenu; id: INTEGER); BEGIN K.SendIPC(mainTID, id) @@ -1011,7 +1094,9 @@ VAR gotoVal: INTEGER; BEGIN EditBox_GetValue(GotoEdit, gotoText); - IF U.str2int(gotoText, gotoVal) & T.goto(text, gotoVal) THEN END + IF U.str2int(gotoText, gotoVal) & T.goto(text, gotoVal) THEN + SetFocus(GotoEdit, FALSE) + END END goto; @@ -1034,10 +1119,165 @@ BEGIN END ShowSearchPanel; -PROCEDURE receiveIPC; +PROCEDURE CanvasToScreen (VAR x, y: INTEGER); +VAR + cliX, cliY, + winX, winY: INTEGER; BEGIN - IF IPC[0] # Menu.lastTID THEN - IPC[2] := 0 + K.WinPos(winX, winY); + K.ClientPos(cliX, cliY); + x := x + winX + cliX + LEFT; + y := y + winY + cliY + TOP; +END CanvasToScreen; + + +PROCEDURE ShowMenu (menu: Menu.tMenu); +VAR + winX, winY, cliX, cliY, x, y, i: INTEGER; + selected, enabled: BOOLEAN; +BEGIN + IF menu = context THEN + mouse(x, y); + IF ~((0 <= x) & (x < canvas.width) & (0 <= y) & (y < canvas.height)) THEN + menu := NIL + END + ELSE + K.WinPos(winX, winY); + K.ClientPos(cliX, cliY); + x := winX + cliX; + y := MainMenuHeight + winY + cliY + END; + + selected := T.selected(text); + + IF menu = menuFile THEN + Menu.setEnabled(menu, menuSave, text.modified); + Menu.setEnabled(menu, menuFolder, text.fileName # ""); + i := 0; + WHILE (i < textsCount) & ~texts[i].modified DO + INC(i) + END; + Menu.setEnabled(menu, menuSaveAll, i < textsCount); + INC(x, menuFileX) + ELSIF (menu = menuEdit) OR (menu = context) THEN + Menu.setEnabled(menu, menuUndo, ~ChangeLog.isFirstGuard(text.edition)); + Menu.setEnabled(menu, menuRedo, ~ChangeLog.isLastGuard(text.edition)); + Menu.setEnabled(menu, menuCut, selected); + Menu.setEnabled(menu, menuCopy, selected); + Menu.setEnabled(menu, menuDelete, selected); + Menu.setEnabled(menu, menuPaste, CB.available()); + IF menu = menuEdit THEN + INC(x, menuEditX) + ELSE + IF y + menu.height >= canvas.height THEN + DEC(y, menu.height) + END; + IF x + menu.width >= canvas.width THEN + DEC(x, menu.width) + END; + CanvasToScreen(x, y) + END + ELSIF menu = menuEncoding THEN + Menu.setCheck(menu, menuUTF8BOM, ORD(text.enc = E.UTF8BOM)*2); + Menu.setCheck(menu, menuUTF8, ORD(text.enc = E.UTF8)*2); + Menu.setCheck(menu, menuCP866, ORD(text.enc = E.CP866)*2); + Menu.setCheck(menu, menuWin1251, ORD(text.enc = E.W1251)*2); + INC(x, menuEncodingX) + ELSIF menu = menuView THEN + Menu.setCheck(menu, menuNumbers, ORD(text.numbers)); + Menu.setCheck(menu, menuFontSize, ORD(font = font2)); + FOR i := 0 TO Ini.sections.count - 1 DO + Menu.setCheck(menu, menuColors + i, ORD(Ini.curSectionNum = i)*2) + END; + INC(x, menuViewX) + ELSIF menu = menuSyntax THEN + Menu.setCheck(menu, menuNone, ORD(text.lang = Languages.langNone)*2); + Menu.setCheck(menu, menuC, ORD(text.lang = Languages.langC)*2); + Menu.setCheck(menu, menuFasm, ORD(text.lang = Languages.langFasm)*2); + Menu.setCheck(menu, menuIni, ORD(text.lang = Languages.langIni)*2); + Menu.setCheck(menu, menuJSON, ORD(text.lang = Languages.langJSON)*2); + Menu.setCheck(menu, menuLua, ORD(text.lang = Languages.langLua)*2); + Menu.setCheck(menu, menuOberon, ORD(text.lang = Languages.langOberon)*2); + Menu.setCheck(menu, menuPascal, ORD(text.lang = Languages.langPascal)*2); + INC(x, menuSyntaxX) + ELSIF menu = menuProgram THEN + Menu.setEnabled(menu, menuBuild, buildScript # ""); + Menu.setEnabled(menu, menuDebug, debugScript # ""); + Menu.setEnabled(menu, menuRun, runScript # ""); + INC(x, menuProgramX) + ELSIF menu = menuTools THEN + INC(x, menuToolsX) + ELSIF menu = menuSearch THEN + INC(x, menuSearchX) + END; + + enabled := search & (searchText # ""); + Menu.setEnabled(menuSearch, menuFindNext, enabled); + Menu.setEnabled(menuSearch, menuFindPrev, enabled); + + Menu.setEnabled(subIndent, menuIncInd, selected); + Menu.setEnabled(subIndent, menuDecInd, selected); + + Menu.setEnabled(subCase, menuUpper, selected); + Menu.setEnabled(subCase, menuLower, selected); + + IF menu # NIL THEN + Timer.stop; + IF Menu.opened(menu) THEN + Menu.close(menu) + END; + Menu.open(menu, x, y) + END; +END ShowMenu; + + +PROCEDURE receiveIPC; +VAR + scrollIPC: BOOLEAN; +BEGIN + scrollIPC := FALSE; + + IF IPC[0] = Timer.ID THEN + T.toggleCursor; + repaint; + IPC[2] := 0 + ELSIF IPC[0] = mainTID THEN + IF IPC[2] = Scroll.ScrollIPC THEN + Scroll.change(hScroll); + IF hScroll.Dec THEN + T.scroll(text, -1, 0) + ELSIF hScroll.Inc THEN + T.scroll(text, 1, 0) + END; + Scroll.change(vScroll); + IF vScroll.Dec THEN + T.scroll(text, 0, -1) + ELSIF vScroll.Inc THEN + T.scroll(text, 0, 1) + END; + repaint; + + IF (0 IN K.MouseState()) THEN + WHILE (0 IN K.MouseState()) & (delay > 0) DO + K.Pause(1); + DEC(delay) + END; + IF delay = 0 THEN + IPC[0] := 0; + IPC[1] := 0; + scrollIPC := TRUE; + Scroll.SendIPC; + delay := 2 + ELSE + delay := scrollDelay + END + ELSE + delay := scrollDelay + END + END; + IPC[2] := 0 + ELSIF ~Menu.isSender(IPC[0]) THEN + IPC[2] := 0 END; CASE IPC[2] OF |0: @@ -1072,12 +1312,22 @@ BEGIN |menuRedo: T.redo(text); repaint - |menuSearch: + |menuFind: ShowSearchPanel; SetFocus(FindEdit, TRUE) + |menuFindNext: + notFound := ~T.findNext(text, box_lib.check_box_get_value(BKW)) + |menuFindPrev: + notFound := ~T.findNext(text, ~box_lib.check_box_get_value(BKW)) |menuGoto: ShowSearchPanel; SetFocus(GotoEdit, TRUE) + |menuToggleBookmark: + T.toggleLabel(text) + |menuNextBookmark: + T.gotoLabel(text, TRUE) + |menuPrevBookmark: + T.gotoLabel(text, FALSE) |menuNumbers: T.toggleNumbers(text) |menuFontSize: @@ -1095,6 +1345,8 @@ BEGIN T.setLang(text, Languages.langFasm) |menuIni: T.setLang(text, Languages.langIni) + |menuJSON: + T.setLang(text, Languages.langJSON) |menuLua: T.setLang(text, Languages.langLua) |menuOberon: @@ -1129,9 +1381,27 @@ BEGIN K.Run("/rd/1/docpack", "f") |menuColors..menuMaxColors: Ini.selectSection(IPC[2] - menuColors) + |menuMoveUp: + T.upLine(text) + |menuMoveDown: + T.downLine(text) + |menuDuplicate: + T.dupLine(text) + |menuRemove: + T.delLine(text) + |menuIncInd: + T.Indent(text, TRUE) + |menuDecInd: + T.Indent(text, FALSE) + |menuUpper: + T.chCase(text, TRUE) + |menuLower: + T.chCase(text, FALSE) END; - IPC[0] := 0; - IPC[1] := 0 + IF ~scrollIPC THEN + IPC[0] := 0; + IPC[1] := 0 + END END receiveIPC; @@ -1147,7 +1417,7 @@ BEGIN |17: menuItem := menuClose |21: menuItem := menuRedo |30: menuItem := menuSelectAll - |33: menuItem := menuSearch + |33: menuItem := menuFind |34: menuItem := menuGoto |44: menuItem := menuUndo |45: menuItem := menuCut @@ -1219,22 +1489,42 @@ VAR menu: List.tList; BEGIN menu := List.create(NIL); - Menu.AddMenuItem(menu, menuUndo, "undo ctrl-Z"); - Menu.AddMenuItem(menu, menuRedo, "redo ctrl-Y"); + Menu.AddMenuItem(menu, menuUndo, "undo ctrl-Z"); + Menu.AddMenuItem(menu, menuRedo, "redo ctrl-Y"); Menu.delimiter(menu); - Menu.AddMenuItem(menu, menuSearch, "search ctrl-F"); - Menu.AddMenuItem(menu, menuGoto, "go to line ctrl-G"); - Menu.delimiter(menu); - Menu.AddMenuItem(menu, menuCut, "cut ctrl-X"); - Menu.AddMenuItem(menu, menuCopy, "copy ctrl-C"); - Menu.AddMenuItem(menu, menuPaste, "paste ctrl-V"); + Menu.AddMenuItem(menu, menuCut, "cut ctrl-X"); + Menu.AddMenuItem(menu, menuCopy, "copy ctrl-C"); + Menu.AddMenuItem(menu, menuPaste, "paste ctrl-V"); Menu.AddMenuItem(menu, menuDelete, "delete"); Menu.delimiter(menu); - Menu.AddMenuItem(menu, menuSelectAll, "select all ctrl-A"); + Menu.AddMenuItem(menu, menuSelectAll, "select all ctrl-A"); + Menu.delimiter(menu); + Menu.AddMenuItem(menu, menuCurLine, "current line"); + Menu.child(menu, subCurLine); + Menu.AddMenuItem(menu, menuIndent, "indent"); + Menu.child(menu, subIndent); + Menu.AddMenuItem(menu, menuCase, "case"); + Menu.child(menu, subCase); RETURN Menu.create(menu, MenuItemClick, MenuKeyDown) END CreateMenuEdit; +PROCEDURE CreateMenuSearch (): Menu.tMenu; +VAR + menu: List.tList; +BEGIN + menu := List.create(NIL); + Menu.AddMenuItem(menu, menuFind, "search ctrl-F"); + Menu.AddMenuItem(menu, menuFindNext, "find next F3"); + Menu.AddMenuItem(menu, menuFindPrev, "find previous shift-F3"); + Menu.AddMenuItem(menu, menuGoto, "go to line ctrl-G"); + Menu.delimiter(menu); + Menu.AddMenuItem(menu, menuBookmark, "bookmark"); + Menu.child(menu, subBookmark); + RETURN Menu.create(menu, MenuItemClick, MenuKeyDown) +END CreateMenuSearch; + + PROCEDURE CreateMenuEncoding (): Menu.tMenu; VAR menu: List.tList; @@ -1279,6 +1569,7 @@ BEGIN Menu.AddMenuItem(menu, menuC, "C"); Menu.AddMenuItem(menu, menuFasm, "Fasm"); Menu.AddMenuItem(menu, menuIni, "Ini"); + Menu.AddMenuItem(menu, menuJSON, "JSON"); Menu.AddMenuItem(menu, menuLua, "Lua"); Menu.AddMenuItem(menu, menuOberon, "Oberon"); Menu.AddMenuItem(menu, menuPascal, "Pascal"); @@ -1293,7 +1584,7 @@ VAR menu: List.tList; BEGIN menu := List.create(NIL); - Menu.AddMenuItem(menu, menuBuild, "build ctrl+F9"); + Menu.AddMenuItem(menu, menuBuild, "build ctrl-F9"); Menu.AddMenuItem(menu, menuBuildScript, "script"); Menu.delimiter(menu); Menu.AddMenuItem(menu, menuRun, "run F9"); @@ -1317,101 +1608,51 @@ BEGIN END CreateMenuTools; -PROCEDURE CanvasToScreen (VAR x, y: INTEGER); +PROCEDURE CreateMenuCurLine(): Menu.tMenu; VAR - cliX, cliY, - winX, winY: INTEGER; + menu: List.tList; BEGIN - K.WinPos(winX, winY); - K.ClientPos(cliX, cliY); - x := x + winX + cliX + LEFT; - y := y + winY + cliY + TOP; -END CanvasToScreen; + menu := List.create(NIL); + Menu.AddMenuItem(menu, menuMoveUp, "move up ctrl-Up"); + Menu.AddMenuItem(menu, menuMoveDown, "move down ctrl-Down"); + Menu.AddMenuItem(menu, menuDuplicate, "duplicate ctrl-D"); + Menu.AddMenuItem(menu, menuRemove, "remove ctrl-Del"); + RETURN Menu.create(menu, MenuItemClick, MenuKeyDown) +END CreateMenuCurLine; -PROCEDURE ShowMenu (menu: Menu.tMenu); +PROCEDURE CreateMenuIndent(): Menu.tMenu; VAR - winX, winY, cliX, cliY, x, y, i: INTEGER; - selected: BOOLEAN; + menu: List.tList; BEGIN - IF menu # context THEN - K.WinPos(winX, winY); - K.ClientPos(cliX, cliY); - x := winX + cliX; - y := MainMenuHeight + winY + cliY - ELSE - mouse(x, y); - IF ~((0 <= x) & (x < canvas.width) & (0 <= y) & (y < canvas.height)) THEN - menu := NIL - END - END; + menu := List.create(NIL); + Menu.AddMenuItem(menu, menuIncInd, "increase Tab"); + Menu.AddMenuItem(menu, menuDecInd, "decrease shift-Bksp"); + RETURN Menu.create(menu, MenuItemClick, MenuKeyDown) +END CreateMenuIndent; - IF menu = menuFile THEN - Menu.setEnabled(menu, menuSave, text.modified); - Menu.setEnabled(menu, menuFolder, text.fileName # ""); - i := 0; - WHILE (i < textsCount) & ~texts[i].modified DO - INC(i) - END; - Menu.setEnabled(menu, menuSaveAll, i < textsCount); - INC(x, menuFileX) - ELSIF (menu = menuEdit) OR (menu = context) THEN - Menu.setEnabled(menu, menuUndo, ~ChangeLog.isFirstGuard(text.edition)); - Menu.setEnabled(menu, menuRedo, ~ChangeLog.isLastGuard(text.edition)); - selected := T.selected(text); - Menu.setEnabled(menu, menuCut, selected); - Menu.setEnabled(menu, menuCopy, selected); - Menu.setEnabled(menu, menuDelete, selected); - Menu.setEnabled(menu, menuPaste, CB.available()); - IF menu = menuEdit THEN - INC(x, menuEditX) - ELSE - IF y + menu.height >= canvas.height THEN - DEC(y, menu.height) - END; - IF x + menu.width >= canvas.width THEN - DEC(x, menu.width) - END; - CanvasToScreen(x, y) - END - ELSIF menu = menuEncoding THEN - Menu.setCheck(menu, menuUTF8BOM, ORD(text.enc = E.UTF8BOM)*2); - Menu.setCheck(menu, menuUTF8, ORD(text.enc = E.UTF8)*2); - Menu.setCheck(menu, menuCP866, ORD(text.enc = E.CP866)*2); - Menu.setCheck(menu, menuWin1251, ORD(text.enc = E.W1251)*2); - INC(x, menuEncodingX) - ELSIF menu = menuView THEN - Menu.setCheck(menu, menuNumbers, ORD(text.numbers)); - Menu.setCheck(menu, menuFontSize, ORD(font = font2)); - FOR i := 0 TO Ini.sections.count - 1 DO - Menu.setCheck(menu, menuColors + i, ORD(Ini.curSectionNum = i)*2) - END; - INC(x, menuViewX) - ELSIF menu = menuSyntax THEN - Menu.setCheck(menu, menuNone, ORD(text.lang = Languages.langNone)*2); - Menu.setCheck(menu, menuC, ORD(text.lang = Languages.langC)*2); - Menu.setCheck(menu, menuFasm, ORD(text.lang = Languages.langFasm)*2); - Menu.setCheck(menu, menuIni, ORD(text.lang = Languages.langIni)*2); - Menu.setCheck(menu, menuLua, ORD(text.lang = Languages.langLua)*2); - Menu.setCheck(menu, menuOberon, ORD(text.lang = Languages.langOberon)*2); - Menu.setCheck(menu, menuPascal, ORD(text.lang = Languages.langPascal)*2); - INC(x, menuSyntaxX) - ELSIF menu = menuProgram THEN - Menu.setEnabled(menu, menuBuild, buildScript # ""); - Menu.setEnabled(menu, menuDebug, debugScript # ""); - Menu.setEnabled(menu, menuRun, runScript # ""); - INC(x, menuProgramX) - ELSIF menu = menuTools THEN - INC(x, menuToolsX) - END; - IF menu # NIL THEN - IF Menu.opened(menu) THEN - Menu.close(menu) - END; - Menu.open(menu, x, y) - END -END ShowMenu; +PROCEDURE CreateMenuCase(): Menu.tMenu; +VAR + menu: List.tList; +BEGIN + menu := List.create(NIL); + Menu.AddMenuItem(menu, menuUpper, "UPPER ctrl-U"); + Menu.AddMenuItem(menu, menuLower, "lower ctrl-L"); + RETURN Menu.create(menu, MenuItemClick, MenuKeyDown) +END CreateMenuCase; + + +PROCEDURE CreateMenuBookmark(): Menu.tMenu; +VAR + menu: List.tList; +BEGIN + menu := List.create(NIL); + Menu.AddMenuItem(menu, menuToggleBookmark, "toggle ctrl-F2"); + Menu.AddMenuItem(menu, menuNextBookmark, "next F2"); + Menu.AddMenuItem(menu, menuPrevBookmark, "previous shift-F2"); + RETURN Menu.create(menu, MenuItemClick, MenuKeyDown) +END CreateMenuBookmark; PROCEDURE KeyDown (key: INTEGER); @@ -1460,6 +1701,9 @@ BEGIN END ELSIF (keyCode = 55) & (key DIV 256 MOD 256 = 52) THEN (* PrtScn *) key := -1 + ELSIF (keyCode = 14) & shift THEN (* shift+Backspace *) + key := -1; + T.input(text, -8) ELSIF (keyCode = 15) & ctrl THEN (* ctrl+Tab *) key := -1; SwitchTab((curText + 1) MOD textsCount) @@ -1498,7 +1742,8 @@ BEGIN |80: key := 40 |75: key := 37 |77: key := 39 - |82: key := -1 (* insert *) + |82, 91, 219, 92, 220: key := -1 (* insert, windows *) + |93: ShowMenu(context); key := -1 |83: key := 46 |59, 62..66, 68, 87, 88: key := -1 (* F1, F4..F8, F10, F11, F12 *) ELSE @@ -1575,6 +1820,8 @@ BEGIN ShowMenu(menuFile) |btnEdit: ShowMenu(menuEdit) + |btnMenuSearch: + ShowMenu(menuSearch) |btnEncoding: ShowMenu(menuEncoding) |btnView: @@ -1638,18 +1885,6 @@ BEGIN Script(buildScript) |btnRun: Script(runScript) - |btnUp: - T.scroll(text, 0, -1); - repaint - |btnDown: - T.scroll(text, 0, 1); - repaint - |btnLeft: - T.scroll(text, -1, 0); - repaint - |btnRight: - T.scroll(text, 1, 0); - repaint |btnFindNext: IF searchText # "" THEN notFound := ~T.findNext(text, box_lib.check_box_get_value(BKW)); @@ -1686,20 +1921,14 @@ VAR focus := EditBox_Focus(eb); box_lib.edit_box_mouse(eb); IF focus # EditBox_Focus(eb) THEN - SetFocus(eb, TRUE) + SetFocus(eb, TRUE); + repaint END END EditBox; BEGIN - Menu.close(menuFile); - Menu.close(menuEdit); - Menu.close(menuEncoding); - Menu.close(menuView); - Menu.close(menuSyntax); - Menu.close(menuProgram); - Menu.close(menuTools); - Menu.close(context); + CloseMenu; IF ~resized THEN msState := K.MouseState(); IF ~(0 IN msState) OR (16 IN msState) THEN @@ -1717,20 +1946,20 @@ BEGIN repaint END END; - IF VScroll THEN - mouse(x, y); - Scroll.mouse(vScroll, x, y - scrollWidth); - T.getScroll(text, scrollX, scrollY); - T.scroll(text, 0, vScroll.value - scrollY); - repaint - END; - IF HScroll THEN - mouse(x, y); - Scroll.mouse(hScroll, x - scrollWidth, y); + IF Scroll.isActive(hScroll) THEN + mouse(x, y); + Scroll.MouseMove(hScroll, x + LEFT, y + TOP); T.getScroll(text, scrollX, scrollY); T.scroll(text, hScroll.value - scrollX, 0); - repaint - END; + repaint + END; + IF Scroll.isActive(vScroll) THEN + mouse(x, y); + Scroll.MouseMove(vScroll, x + LEFT, y + TOP); + T.getScroll(text, scrollX, scrollY); + T.scroll(text, 0, vScroll.value - scrollY); + repaint + END; IF (8 IN msState) & ~(24 IN msState) THEN mouse(firstClickX, firstClickY); time := click(firstClickX, firstClickY) @@ -1740,9 +1969,13 @@ BEGIN END; IF 24 IN msState THEN mouse(x, y); + IF ~hScroll.mouse THEN + Scroll.MouseDown(hScroll, x + LEFT, y + TOP) + END; + IF ~vScroll.mouse THEN + Scroll.MouseDown(vScroll, x + LEFT, y + TOP) + END; IF (ABS(x - firstClickX) < 5) & (ABS(y - firstClickY) < 5) THEN - VScroll := FALSE; - HScroll := FALSE; IF (0 <= x) & (x < canvas.width) & (0 <= y) & (y < canvas.height) THEN leftButton := FALSE; T.selectWord(text); @@ -1808,6 +2041,7 @@ VAR resized: BOOLEAN; firstClickX, firstClickY, time: INTEGER; BEGIN + delay := scrollDelay; K.GetSystemColors; Icons.get(icons, grayIcons); modified := FALSE; @@ -1836,17 +2070,28 @@ BEGIN font2 := G.CreateFont(2, "", {}); font := font1; G.SetFont(canvas, font); - T.init(NIL); + T.init(resetTimer); T.setCanvas(canvas); U.ptr2str(K.GetCommandLine(), fileName); context := CreateContextMenu(); menuFile := CreateMenuFile(); + subCurLine := CreateMenuCurLine(); + subIndent := CreateMenuIndent(); + subCase := CreateMenuCase(); menuEdit := CreateMenuEdit(); + subCurLine.parent := menuEdit; + subIndent.parent := menuEdit; + subCase.parent := menuEdit; + subBookmark := CreateMenuBookmark(); + menuSearch := CreateMenuSearch(); + subBookmark.parent := menuSearch; menuEncoding := CreateMenuEncoding(); menuView := CreateMenuView(); menuSyntax := CreateMenuSyntax(); menuProgram := CreateMenuProgram(); menuTools := CreateMenuTools(); + + Ini.getSettings(buildScript, runScript, debugScript); IF fileName = "" THEN text := T.New(); filePath := "/rd/1" @@ -1854,16 +2099,15 @@ BEGIN text := T.open(fileName, err); IF text = NIL THEN error("'cedit: error opening file' -E"); - K.Exit + Exit ELSE U.getPath(fileName, filePath) END END; OD := OpenDlg.Create(draw_window, OpenDlg.topen, filePath, ""); insert(0, text); - - vScroll := Scroll.create(scrollWidth, canvas.height - scrollWidth*2 + 2); - hScroll := Scroll.create(canvas.width - scrollWidth*2, scrollWidth); + Scroll.create(FALSE, canvas.width, scrollWidth, scrollWidth, scrollWidth, hScroll); + Scroll.create(TRUE, scrollWidth, canvas.height, scrollWidth, scrollWidth, vScroll); T.resize(canvas.width, canvas.height); T.SetPos(text, 0, 0); confirm := FALSE; @@ -1876,19 +2120,23 @@ BEGIN cs := FALSE; whole := FALSE; replaced := 0; - Ini.getStr("paths", "build", buildScript); - Ini.getStr("paths", "run", runScript); - Ini.getStr("paths", "debug", debugScript); K.SetEventsMask({0, 1, 2, 5, 6, 31}); + Menu.init(resetTimer); draw_window; + Timer.create(mainTID); WHILE TRUE DO CASE K.WaitForEvent() OF - |1: Redraw(resized, width, height, cliWidth, cliHeight) + |1: IF Menu.redraw THEN + Redraw(resized, width, height, cliWidth, cliHeight) + ELSE + Menu.Redraw; + K.CreateWindow(30 + K.GetTickCount() MOD 128, 30 + K.GetTickCount() MOD 128, winWidth, winHeight, K.winColor, 73H, 0, 0, header); + repaint + END |2: KeyDown(K.GetKey()) |3: BtnClick |6: MouseEvent(resized, firstClickX, firstClickY, time) |7: receiveIPC - ELSE END END END main; diff --git a/programs/develop/cedit/SRC/Clipboard.ob07 b/programs/develop/cedit/SRC/Clipboard.ob07 index 11953327e8..936a67d19f 100644 --- a/programs/develop/cedit/SRC/Clipboard.ob07 +++ b/programs/develop/cedit/SRC/Clipboard.ob07 @@ -24,6 +24,7 @@ IMPORT SYSTEM, K := KOSAPI, E := Encodings, Lines; CONST TTEXT = 0; lenEOL* = 2; + TAB = 9X; TYPE tBuffer* = POINTER TO RECORD @@ -53,18 +54,22 @@ BEGIN size := cnt + 12; a := K.malloc(size); ASSERT(a # 0); - SYSTEM.PUT32(a, size); SYSTEM.PUT32(a + 4, TTEXT); SYSTEM.PUT32(a + 8, 1); pchar := a + 12; ptr := buffer.dataPtr; WHILE cnt > 0 DO SYSTEM.GET(ptr, wch); - SYSTEM.PUT(pchar, CHR(E.UNI[ORD(wch), E.CP866] MOD 256)); - INC(pchar); + IF wch # Lines.TAB1 THEN + SYSTEM.PUT(pchar, CHR(E.UNI[ORD(wch), E.CP866] MOD 256)); + INC(pchar) + ELSE + DEC(size); + END; INC(ptr, 2); DEC(cnt) END; + SYSTEM.PUT32(a, size); K.sysfunc2(54, 3); K.sysfunc4(54, 2, size, a) END put; diff --git a/programs/develop/cedit/SRC/File.ob07 b/programs/develop/cedit/SRC/File.ob07 index e49cdd3d5d..db59ae6a69 100644 --- a/programs/develop/cedit/SRC/File.ob07 +++ b/programs/develop/cedit/SRC/File.ob07 @@ -284,6 +284,8 @@ BEGIN IF outFile # NIL THEN IF size # 0 THEN res := Write(outFile, buf, size) = size + ELSE + res := TRUE END; Close(outFile) END; diff --git a/programs/develop/cedit/SRC/Graph.ob07 b/programs/develop/cedit/SRC/Graph.ob07 index d9fc8f3c1e..5ea7873401 100644 --- a/programs/develop/cedit/SRC/Graph.ob07 +++ b/programs/develop/cedit/SRC/Graph.ob07 @@ -19,7 +19,7 @@ MODULE Graph; -IMPORT SYSTEM, K := KOSAPI; +IMPORT SYSTEM, K := KOSAPI, Lines; CONST @@ -27,6 +27,11 @@ CONST modeNOT = 1; modeXOR = 2; + triUp* = FALSE; + triDown* = TRUE; + triLeft* = FALSE; + triRight* = TRUE; + TYPE tFont* = POINTER TO RECORD @@ -177,6 +182,35 @@ BEGIN END DLine; +PROCEDURE Triangle* (canvas: tCanvas; x1, y1, x2, y2: INTEGER; orientation: BOOLEAN); +VAR + i, a, b, d: INTEGER; +BEGIN + d := ORD(orientation)*2 - 1; + IF y1 = y2 THEN + i := y1; + a := MIN(x1, x2); + b := MAX(x1, x2); + WHILE a <= b DO + HLine(canvas, i, a, b); + INC(i, d); + INC(a); + DEC(b) + END + ELSIF x1 = x2 THEN + i := x1; + a := MIN(y1, y2); + b := MAX(y1, y2); + WHILE a <= b DO + VLine(canvas, i, a, b); + INC(i, d); + INC(a); + DEC(b) + END + END +END Triangle; + + PROCEDURE FillRect* (canvas: tCanvas; left, top, right, bottom: INTEGER); VAR y: INTEGER; @@ -218,6 +252,7 @@ CONST WCHAR_SIZE = 2; VAR color, i: INTEGER; + c: WCHAR; BEGIN IF (0 <= y) & (y <= canvas.height - canvas.font.height - 1) THEN IF x < 0 THEN @@ -233,13 +268,16 @@ BEGIN canvas.color := canvas.backColor; FillRect(canvas, x, y, x + n*canvas.font.width, y + canvas.font.height); canvas.color := color; -(* WHILE n > 0 DO - K.sysfunc6(4, x*65536 + y, LSL(28H + canvas.font.size, 24) + canvas.textColor, text + i*WCHAR_SIZE, 1, canvas.bitmap - 8); + WHILE n > 0 DO + SYSTEM.GET(text + i*WCHAR_SIZE, c); + IF ~Lines.isSpace(c) THEN + K.sysfunc6(4, x*65536 + y, LSL(28H + canvas.font.size, 24) + canvas.textColor, SYSTEM.ADR(c), 1, canvas.bitmap - 8) + END; INC(x, canvas.font.width); INC(i); DEC(n) - END*) - K.sysfunc6(4, x*65536 + y, LSL(28H + canvas.font.size, 24) + canvas.textColor, text + i*WCHAR_SIZE, n, canvas.bitmap - 8) + END + (*K.sysfunc6(4, x*65536 + y, LSL(28H + canvas.font.size, 24) + canvas.textColor, text + i*WCHAR_SIZE, n, canvas.bitmap - 8)*) END END END TextOut; diff --git a/programs/develop/cedit/SRC/Icons.ob07 b/programs/develop/cedit/SRC/Icons.ob07 index 5fd2f5b3d6..bef52f202e 100644 --- a/programs/develop/cedit/SRC/Icons.ob07 +++ b/programs/develop/cedit/SRC/Icons.ob07 @@ -1,4 +1,4 @@ -(* +(* Copyright 2021 Anton Krotov This file is part of CEdit. diff --git a/programs/develop/cedit/SRC/Ini.ob07 b/programs/develop/cedit/SRC/Ini.ob07 index bc6524d56f..33419be133 100644 --- a/programs/develop/cedit/SRC/Ini.ob07 +++ b/programs/develop/cedit/SRC/Ini.ob07 @@ -21,7 +21,7 @@ MODULE Ini; IMPORT - KOSAPI, SYSTEM, RW, Text, Utils, File, List, Languages, KolibriOS; + KOSAPI, SYSTEM, RW, Text, Utils, File, List, Languages, KolibriOS, Lines; CONST @@ -46,7 +46,7 @@ TYPE VAR - get_color: PROCEDURE [stdcall] (f_name: RW.tFileName; sec_name: tASCIISectionName; key_name: tString; def_val: INTEGER): INTEGER; + get_color, get_int: PROCEDURE [stdcall] (f_name: RW.tFileName; sec_name: tASCIISectionName; key_name: tString; def_val: INTEGER): INTEGER; get_str: PROCEDURE [stdcall] (f_name, sec_name, key_name, buffer, buf_len, def_val: INTEGER): INTEGER; enum_sections: PROCEDURE [stdcall] (f_name: RW.tFileName; callback: INTEGER); @@ -55,6 +55,7 @@ VAR curSection*: tASCIISectionName; curSectionNum*: INTEGER; + blink*: INTEGER; PROCEDURE getColor (key: tString; def: INTEGER): INTEGER; @@ -145,6 +146,21 @@ BEGIN END selectSection; +PROCEDURE getSettings* (VAR build, run, debug: RW.tFileName); +BEGIN + Lines.setTabs(get_int(IniFileName, "settings", "tab", 4)); + blink := get_int(IniFileName, "settings", "blink", 70); + IF blink = 0 THEN + blink := -1 + ELSE + blink := MIN(MAX(blink, 1), 1000) + END; + getStr("settings", "build", build); + getStr("settings", "run", run); + getStr("settings", "debug", debug) +END getSettings; + + PROCEDURE load* (path: RW.tFileName); VAR Lib: INTEGER; @@ -160,16 +176,18 @@ VAR BEGIN sections := List.create(NIL); - IF File.Exists("/rd/1/settings/cedit.ini") THEN - IniFileName := "/rd/1/settings/cedit.ini" - ELSE - Utils.getPath(path, IniFileName); - Utils.append8(IniFileName, Utils.SLASH); - Utils.append8(IniFileName, fileName); + + Utils.getPath(path, IniFileName); + Utils.append8(IniFileName, Utils.SLASH); + Utils.append8(IniFileName, fileName); + + IF ~File.Exists(IniFileName) THEN + IniFileName := "/rd/1/settings/cedit.ini" END; Lib := KOSAPI.LoadLib("/rd/1/Lib/Libini.obj"); GetProc(Lib, SYSTEM.ADR(get_color), "ini_get_color"); + GetProc(Lib, SYSTEM.ADR(get_int), "ini_get_int"); GetProc(Lib, SYSTEM.ADR(get_str), "ini_get_str"); GetProc(Lib, SYSTEM.ADR(enum_sections), "ini_enum_sections"); diff --git a/programs/develop/cedit/SRC/KolibriOS.ob07 b/programs/develop/cedit/SRC/KolibriOS.ob07 index efdad9a74d..77e480c94f 100644 --- a/programs/develop/cedit/SRC/KolibriOS.ob07 +++ b/programs/develop/cedit/SRC/KolibriOS.ob07 @@ -319,13 +319,13 @@ END malloc; PROCEDURE SetIPC* (buffer: ARRAY OF INTEGER); BEGIN - KOSAPI.sysfunc4(60, 1, SYSTEM.ADR(buffer[0]), LEN(buffer)*SYSTEM.SIZE(INTEGER)); + KOSAPI.sysfunc4(60, 1, SYSTEM.ADR(buffer[0]), LEN(buffer)*SYSTEM.SIZE(INTEGER)) END SetIPC; PROCEDURE SendIPC* (tid, msg: INTEGER); BEGIN - KOSAPI.sysfunc5(60, 2, tid, SYSTEM.ADR(msg), SYSTEM.SIZE(INTEGER)) + KOSAPI.sysfunc5(60, 2, tid, SYSTEM.ADR(msg), SYSTEM.SIZE(INTEGER)) END SendIPC; diff --git a/programs/develop/cedit/SRC/Languages.ob07 b/programs/develop/cedit/SRC/Languages.ob07 index ffafb951a6..7a197c47da 100644 --- a/programs/develop/cedit/SRC/Languages.ob07 +++ b/programs/develop/cedit/SRC/Languages.ob07 @@ -25,9 +25,9 @@ IMPORT Lines; CONST langNone* = 0; langC* = 1; langOberon* = 2; langPascal* = 3; - langFasm* = 4; langLua* = 5; langIni* = 6; + langFasm* = 4; langLua* = 5; langIni* = 6; langJSON* = 7; - csLang = {langC, langOberon, langLua, langIni}; + csLang = {langC, langOberon, langLua, langIni, langJSON}; TYPE @@ -42,7 +42,7 @@ TYPE VAR - oberonKW, cKW, pascalKW, luaKW, iniKW, fasmKW: ARRAY 3 OF tKeyWords; + oberonKW, cKW, pascalKW, luaKW, iniKW, fasmKW, jsonKW: ARRAY 3 OF tKeyWords; PROCEDURE isCS* (lang: INTEGER): BOOLEAN; @@ -66,14 +66,16 @@ PROCEDURE isKey* (s: ARRAY OF WCHAR; lang, kwSet: INTEGER): BOOLEAN; VAR res: BOOLEAN; BEGIN + DEC(kwSet); res := FALSE; CASE lang OF - |langC: res := checkKW(s, cKW[kwSet - 1]) - |langOberon: res := checkKW(s, oberonKW[kwSet - 1]) - |langPascal: res := checkKW(s, pascalKW[kwSet - 1]) - |langLua: res := checkKW(s, luaKW[kwSet - 1]) - |langIni: res := checkKW(s, iniKW[kwSet - 1]) - |langFasm: res := checkKW(s, fasmKW[kwSet - 1]) + |langC: res := checkKW(s, cKW[kwSet]) + |langOberon: res := checkKW(s, oberonKW[kwSet]) + |langPascal: res := checkKW(s, pascalKW[kwSet]) + |langLua: res := checkKW(s, luaKW[kwSet]) + |langIni: res := checkKW(s, iniKW[kwSet]) + |langFasm: res := checkKW(s, fasmKW[kwSet]) + |langJSON: res := checkKW(s, jsonKW[kwSet]) END RETURN res END isKey; @@ -317,7 +319,8 @@ BEGIN CASE lang OF |langNone: |langFasm: - |langC: C(line, depth, cond, pos, n) + |langC, + langJSON: C(line, depth, cond, pos, n) |langOberon: Oberon(line, depth, cond, pos, n) |langPascal: Pascal(line, depth, cond, pos, n) |langLua: Lua(line, depth, cond, pos, n) @@ -381,6 +384,7 @@ BEGIN loadKW(luaKW, getStr, "lang_Lua"); loadKW(iniKW, getStr, "lang_Ini"); loadKW(fasmKW, getStr, "lang_Fasm"); + loadKW(jsonKW, getStr, "lang_JSON"); END init; diff --git a/programs/develop/cedit/SRC/Lines.ob07 b/programs/develop/cedit/SRC/Lines.ob07 index adba577979..93f2e8da8d 100644 --- a/programs/develop/cedit/SRC/Lines.ob07 +++ b/programs/develop/cedit/SRC/Lines.ob07 @@ -24,7 +24,9 @@ IMPORT CONST WCHAR_SIZE = 2; - SPACE = 20X; + SPACE* = 20X; + TAB* = 9X; + TAB1* = 0FFFEX; TYPE @@ -51,7 +53,8 @@ VAR (* _typedPtr: PTypedPtr; _untypedPtr: PUntypedPtr;*) - pMaxLength: INTEGER; + pMaxLength, tab*: INTEGER; + tabs*: BOOLEAN; PROCEDURE movInt (VAR v: INTEGER; x: INTEGER); @@ -166,15 +169,28 @@ BEGIN END destroy; -PROCEDURE modify* (line: tLine); +PROCEDURE getChar* (line: tLine; i: INTEGER): WCHAR; +VAR + c: WCHAR; BEGIN - IF ~line.temp THEN - movBool(line.modified, TRUE); - movBool(line.saved, FALSE) - END; - line.modified := TRUE; - line.saved := FALSE -END modify; + SYSTEM.GET(line.ptr + i*WCHAR_SIZE, c) + RETURN c +END getChar; + + +PROCEDURE tabWidth (line: tLine; pos: INTEGER): INTEGER; +VAR + n: INTEGER; +BEGIN + n := pos; + IF getChar(line, pos) = TAB THEN + INC(pos); + WHILE getChar(line, pos) = TAB1 DO + INC(pos) + END + END + RETURN pos - n +END tabWidth; PROCEDURE save* (line: tLine); @@ -188,13 +204,9 @@ BEGIN END save; -PROCEDURE getChar* (line: tLine; i: INTEGER): WCHAR; -VAR - c: WCHAR; -BEGIN - SYSTEM.GET(line.ptr + i*WCHAR_SIZE, c) - RETURN c -END getChar; +PROCEDURE isSpace* (c: WCHAR): BOOLEAN; + RETURN (c = SPACE) OR (c = TAB) OR (c = TAB1) +END isSpace; PROCEDURE trimLength* (line: tLine): INTEGER; @@ -202,7 +214,7 @@ VAR i: INTEGER; BEGIN i := line.length - 1; - WHILE (i >= 0) & (getChar(line, i) = SPACE) DO + WHILE (i >= 0) & isSpace(getChar(line, i)) DO DEC(i) END RETURN i + 1 @@ -345,6 +357,44 @@ BEGIN END delCharN; +PROCEDURE fixTabs (line: tLine); +VAR + i, n, k: INTEGER; +BEGIN + i := 0; + WHILE i < line.length DO + n := tabWidth(line, i); + IF n # 0 THEN + k := tab - i MOD tab; + IF n > k THEN + delCharN(line, i + 1, n - k) + ELSIF n < k THEN + DEC(k, n); + insert3(line, i + 1, k); + WHILE k > 0 DO + setChar(line, i + 1, TAB1); + INC(i); + DEC(k) + END + END + END; + INC(i) + END +END fixTabs; + + +PROCEDURE modify* (line: tLine); +BEGIN + IF ~line.temp THEN + movBool(line.modified, TRUE); + movBool(line.saved, FALSE) + END; + line.modified := TRUE; + line.saved := FALSE; + fixTabs(line) +END modify; + + PROCEDURE wrap* (line, nextLine: tLine; pos: INTEGER); VAR ptr1, ptr2: INTEGER; @@ -439,6 +489,16 @@ BEGIN END setMaxLength; +PROCEDURE setTabs* (_tab: INTEGER); +BEGIN + IF _tab = 0 THEN + _tab := 4 + END; + tabs := _tab > 0; + tab := ABS(_tab) +END setTabs; + + BEGIN pMaxLength := 0 END Lines. \ No newline at end of file diff --git a/programs/develop/cedit/SRC/Menu.ob07 b/programs/develop/cedit/SRC/Menu.ob07 index 50c76fdaa1..0631c2290a 100644 --- a/programs/develop/cedit/SRC/Menu.ob07 +++ b/programs/develop/cedit/SRC/Menu.ob07 @@ -23,13 +23,15 @@ IMPORT SYSTEM, G := Graph, List, K := KolibriOS; CONST - fontHeight = 20; + fontHeight = 22; fontWidth = 8; RIGHT = 16; LEFT = 16; TOP = 1; + maxLEVEL = 1; + backColor = 0F0F0F0H; foreColor = 0; selBackColor = 091C9F7H; @@ -41,14 +43,11 @@ CONST TYPE - tItem* = POINTER TO RECORD (List.tItem) - id*, check: INTEGER; - text: ARRAY 32 OF WCHAR; - enabled, delim: BOOLEAN - END; tMenu* = POINTER TO RECORD tid*: INTEGER; + active*: BOOLEAN; + parent*, child: tMenu; winX, winY, width*, height*: INTEGER; selItem, cliItem: INTEGER; @@ -60,24 +59,49 @@ TYPE key: PROCEDURE (menu: tMenu; key: INTEGER): BOOLEAN END; + tItem* = POINTER TO RECORD (List.tItem) + id*, check: INTEGER; + text: ARRAY 32 OF WCHAR; + enabled, delim: BOOLEAN; + child: tMenu + END; + tClick = PROCEDURE (menu: tMenu; id: INTEGER); tKey = PROCEDURE (menu: tMenu; key: INTEGER): BOOLEAN; + tProc = PROCEDURE; VAR - lastTID*: INTEGER; - stack: ARRAY 250000 OF INTEGER; + stack: ARRAY maxLEVEL + 1, 250000 OF INTEGER; + TIDs: ARRAY maxLEVEL + 1 OF INTEGER; + resetTimer: tProc; + _open: PROCEDURE (m: tMenu; x, y: INTEGER); + redraw*: BOOLEAN; + + +PROCEDURE isSender* (tid: INTEGER): BOOLEAN; +VAR + i: INTEGER; +BEGIN + i := 0; + WHILE (i <= maxLEVEL) & (TIDs[i] # tid) DO + INC(i) + END + RETURN i <= maxLEVEL +END isSender; PROCEDURE exit (m: tMenu); BEGIN m.tid := 0; + m.active := FALSE; + resetTimer; K.Exit END exit; PROCEDURE repaint (m: tMenu); VAR - y, i: INTEGER; + y, i, X, Y1, Y2: INTEGER; item: tItem; BkColor, TextColor: INTEGER; canvas: G.tCanvas; @@ -115,17 +139,23 @@ BEGIN G.SetBkColor(canvas, BkColor); G.TextOut2(canvas, LEFT, y + (fontHeight - 16) DIV 2 - 2, item.text, LENGTH(item.text)); + G.SetColor(canvas, TextColor); IF item.check = 1 THEN - G.SetColor(canvas, TextColor); G.DLine(canvas, 4, 7, y + (fontHeight - 16) DIV 2 + 5, -1); G.DLine(canvas, 4, 7, y + (fontHeight - 16) DIV 2 + 6, -1); G.DLine(canvas, 7, 12, y + (fontHeight - 16) DIV 2 + 8, 1); G.DLine(canvas, 7, 12, y + (fontHeight - 16) DIV 2 + 9, 1); ELSIF item.check = 2 THEN - G.SetColor(canvas, TextColor); G.FillRect(canvas, 6, y + fontHeight DIV 2 - 4, 10, y + fontHeight DIV 2) END; + IF item.child # NIL THEN + X := m.width - 9; + Y1 := y + (fontHeight - 16) DIV 2 + 2; + Y2 := Y1 + 8; + G.Triangle(canvas, X, Y1, X, Y2, G.triRight) + END; + INC(y, fontHeight); IF item.delim THEN G.SetColor(canvas, ORD((-BITS(backColor))*{0..23})); @@ -157,18 +187,77 @@ BEGIN END mouse; +PROCEDURE close* (m: tMenu); +BEGIN + IF (m # NIL) & (m.tid # 0) THEN + IF m.child # NIL THEN + close(m.child); + m.child := NIL + END; + K.ExitID(m.tid); + m.tid := 0; + m.active := FALSE + END +END close; + + PROCEDURE click (m: tMenu; i: INTEGER); VAR item: List.tItem; + p: tMenu; BEGIN item := List.getItem(m.items, i); - IF (item # NIL) & item(tItem).enabled THEN + IF (item # NIL) & item(tItem).enabled & (item(tItem).child = NIL) THEN m.click(m, item(tItem).id); - exit(m) + p := m.parent; + WHILE p # NIL DO + p.child := NIL; + close(p); + p := p.parent + END; + redraw := TRUE; + exit(m) END END click; +PROCEDURE opened* (m: tMenu): BOOLEAN; + RETURN m.tid # 0 +END opened; + + +PROCEDURE isActive (m: tMenu): BOOLEAN; + RETURN (m # NIL) & ((m.tid # 0) & m.active OR isActive(m.child)) +END isActive; + + +PROCEDURE closeChild (m: tMenu); +BEGIN + IF m.child # NIL THEN + redraw := FALSE; + close(m.child); + m.child := NIL + END +END closeChild; + + +PROCEDURE submenu (m: tMenu); +VAR + item: List.tItem; +BEGIN + item := List.getItem(m.items, m.selItem); + IF (item # NIL) & item(tItem).enabled & (item(tItem).child # NIL) THEN + IF ~opened(item(tItem).child) THEN + closeChild(m); + _open(item(tItem).child, m.winX + m.width - 2, m.winY + m.selItem*fontHeight); + m.child := item(tItem).child + END + ELSE + closeChild(m) + END +END submenu; + + PROCEDURE [stdcall] window (m: tMenu); VAR x, y: INTEGER; @@ -199,6 +288,12 @@ BEGIN click(m, m.selItem) END; m.cliItem := -1 + ELSIF key DIV 65536 = 77 THEN + submenu(m) + ELSIF key DIV 65536 = 75 THEN + IF m.parent # NIL THEN + exit(m) + END ELSE IF m.key(m, key) THEN exit(m) @@ -209,6 +304,7 @@ BEGIN msState := K.MouseState(); mouse(m, x, y); IF (0 <= x) & (x < m.width) & (0 <= y) & (y < m.height) THEN + m.active := TRUE; m.selItem := (y - TOP) DIV fontHeight; IF 8 IN msState THEN m.cliItem := (y - TOP) DIV fontHeight @@ -220,17 +316,47 @@ BEGIN m.cliItem := -1 END ELSE + m.active := FALSE; m.cliItem := -1; - IF {8, 9, 10} * msState # {} THEN + IF ({8, 9, 10, 16} * msState # {}) & ~isActive(m.child) THEN exit(m) END END; - repaint(m) + repaint(m); + submenu(m) END END END window; +PROCEDURE level (m: tMenu): INTEGER; +VAR + res: INTEGER; +BEGIN + res := 0; + WHILE m.parent # NIL DO + INC(res); + m := m.parent + END + RETURN res +END level; + + +PROCEDURE open* (m: tMenu; x, y: INTEGER); +VAR + L: INTEGER; +BEGIN + IF m.tid = 0 THEN + m.winX := x; + m.winY := y; + L := level(m); + SYSTEM.PUT(SYSTEM.ADR(stack[L][LEN(stack[0]) - 1]), m); + m.tid := K.CreateThread(SYSTEM.ADR(window), stack[L]); + TIDs[L] := m.tid + END +END open; + + PROCEDURE AddMenuItem* (items: List.tList; id: INTEGER; s: ARRAY OF WCHAR); VAR item: tItem; @@ -240,6 +366,7 @@ BEGIN item.text := s; item.enabled := TRUE; item.delim := FALSE; + item.child := NIL; List.append(items, item); END AddMenuItem; @@ -250,6 +377,12 @@ BEGIN END delimiter; +PROCEDURE child* (items: List.tList; menu: tMenu); +BEGIN + items.last(tItem).child := menu +END child; + + PROCEDURE getItem (m: tMenu; id: INTEGER): tItem; VAR item: tItem; @@ -293,32 +426,6 @@ BEGIN END isEnabled; -PROCEDURE opened* (m: tMenu): BOOLEAN; - RETURN m.tid # 0 -END opened; - - -PROCEDURE open* (m: tMenu; x, y: INTEGER); -BEGIN - IF m.tid = 0 THEN - m.winX := x; - m.winY := y; - SYSTEM.PUT(SYSTEM.ADR(stack[LEN(stack) - 1]), m); - lastTID := K.CreateThread(SYSTEM.ADR(window), stack); - m.tid := lastTID - END -END open; - - -PROCEDURE close* (m: tMenu); -BEGIN - IF m.tid # 0 THEN - K.ExitID(m.tid); - m.tid := 0 - END -END close; - - PROCEDURE create* (items: List.tList; click: tClick; key: tKey): tMenu; VAR m: tMenu; @@ -327,6 +434,9 @@ VAR BEGIN NEW(m); m.tid := 0; + m.active := FALSE; + m.parent := NIL; + m.child := NIL; m.items := items; m.click := click; m.key := key; @@ -345,6 +455,23 @@ BEGIN END create; +PROCEDURE Redraw*; BEGIN - lastTID := 0 + redraw := TRUE +END Redraw; + + +PROCEDURE init* (_resetTimer: tProc); +VAR + i: INTEGER; +BEGIN + Redraw; + resetTimer := _resetTimer; + _open := open; + FOR i := 0 TO maxLEVEL DO + TIDs[i] := 0 + END +END init; + + END Menu. \ No newline at end of file diff --git a/programs/develop/cedit/SRC/RW.ob07 b/programs/develop/cedit/SRC/RW.ob07 index 8ed45d7ba8..12803a818b 100644 --- a/programs/develop/cedit/SRC/RW.ob07 +++ b/programs/develop/cedit/SRC/RW.ob07 @@ -29,8 +29,6 @@ CONST CR = 0DX; LF = 0AX; TAB = 9X; SPACE = 20X; BOM = 0FEFFX; - TAB_SIZE* = 4; - BUF_SIZE = 65536; NAME_LEN = 1024; @@ -166,7 +164,7 @@ PROCEDURE getCharUTF16LE (file: tInput): INTEGER; END getCharUTF16LE; -PROCEDURE getString* (file: tInput; line: Lines.tLine; VAR eol: BOOLEAN): INTEGER; +PROCEDURE getString* (file: tInput; line: Lines.tLine; tabs: BOOLEAN; VAR eol: BOOLEAN): INTEGER; VAR c: WCHAR; i, L, k, n: INTEGER; @@ -178,6 +176,9 @@ BEGIN i := ORD(file.cnt > 0) - 1; WHILE (file.cnt > 0) & ~eol DO c := WCHR(file.getChar(file) MOD 65536); + IF c = Lines.TAB1 THEN + c := SPACE + END; IF c = CR THEN eol := TRUE; file.CR := TRUE @@ -187,9 +188,20 @@ BEGIN END; file.CR := FALSE ELSIF c = TAB THEN - k := TAB_SIZE - i MOD TAB_SIZE; + k := Lines.tab - i MOD Lines.tab; + IF tabs THEN + s[i] := TAB + ELSE + s[i] := SPACE + END; + INC(i); + DEC(k); WHILE k > 0 DO - s[i] := SPACE; + IF tabs THEN + s[i] := Lines.TAB1 + ELSE + s[i] := SPACE + END; INC(i); IF i = L THEN Lines.concat(line, s); @@ -354,9 +366,19 @@ END putByte; PROCEDURE putString* (file: tOutput; line: Lines.tLine; n: INTEGER): INTEGER; VAR i: INTEGER; + c: WCHAR; + err: BOOLEAN; BEGIN i := 0; - WHILE (i < n) & file.putChar(file, ORD(Lines.getChar(line, i))) DO + err := FALSE; + WHILE (i < n) & ~err DO + c := Lines.getChar(line, i); + IF c # Lines.TAB1 THEN + IF ~file.putChar(file, ORD(c)) THEN + err := TRUE; + DEC(i) + END + END; INC(i) END RETURN i diff --git a/programs/develop/cedit/SRC/Tabs.ob07 b/programs/develop/cedit/SRC/Tabs.ob07 index 72142e2793..5301151e73 100644 --- a/programs/develop/cedit/SRC/Tabs.ob07 +++ b/programs/develop/cedit/SRC/Tabs.ob07 @@ -26,8 +26,7 @@ CONST btnID* = 100; tabHeight* = 22; curTabHeight = 26; - scrWidth = 10; - + scrWidth = 15; TYPE @@ -105,7 +104,7 @@ VAR BEGIN y := t.y; x := t.x; - K.DrawRect(x, y - (curTabHeight - tabHeight), t.width + 2*scrWidth, t.height + (curTabHeight - tabHeight) - 1, K.winColor); + K.DrawRect(x, y - (curTabHeight - tabHeight), t.width + (2*scrWidth + 2), t.height + (curTabHeight - tabHeight) - 1, K.winColor); IF Width(t, 0, t.strings.count - 1) > t.width THEN INC(x, 2*scrWidth); K.CreateButton(btnID - 1, t.x, t.y, scrWidth, t.height - 1, K.btnColor, "<"); diff --git a/programs/develop/cedit/SRC/Text.ob07 b/programs/develop/cedit/SRC/Text.ob07 index 13acc2387a..f3434e266b 100644 --- a/programs/develop/cedit/SRC/Text.ob07 +++ b/programs/develop/cedit/SRC/Text.ob07 @@ -33,8 +33,9 @@ IMPORT CONST - SPACE = 20X; - TAB = RW.TAB_SIZE; + SPACE = Lines.SPACE; + TAB = Lines.TAB; + TAB1 = Lines.TAB1; lenEOL = CB.lenEOL; mark_width = 2; @@ -96,7 +97,7 @@ VAR comment, string, num, delim, key1, key2, key3: INTEGER END; canvas: G.tCanvas; - drawCursor*: BOOLEAN; + drawCursor: BOOLEAN; padding: RECORD left, top: INTEGER END; size, textsize: tPoint; charWidth, charHeight: INTEGER; @@ -121,6 +122,8 @@ BEGIN text.lang := Lang.langLua ELSIF ext = "INI" THEN text.lang := Lang.langIni + ELSIF ext = "JSON" THEN + text.lang := Lang.langJSON ELSE text.lang := Lang.langNone END @@ -176,6 +179,18 @@ BEGIN END toggleCursor; +PROCEDURE showCursor*; +BEGIN + drawCursor := TRUE +END showCursor; + + +PROCEDURE hideCursor*; +BEGIN + drawCursor := FALSE +END hideCursor; + + PROCEDURE getChar (line: tLine; i: INTEGER): WCHAR; VAR res: WCHAR; @@ -418,7 +433,7 @@ BEGIN PrintLex(text, line, k, i, y, colors.num, backColor) END - ELSIF lang = Lang.langC THEN + ELSIF (lang = Lang.langC) OR (lang = Lang.langJSON) THEN IF depth = 0 THEN IF c = "/" THEN @@ -434,11 +449,21 @@ BEGIN INC(i); PrintComment(text, line, depth, i, y, backColor); cond := 0 - ELSIF (c = "'") OR (c = '"') THEN - String(text, line, i, y, backColor); - cond := 0 - ELSIF (U.isLetter(c) OR (c = "_")) THEN - ident(text, i, i - ORD((i > 0) & (getChar(line, i - 1) = "#")), y, line, backColor, Lang.isCS(lang)); + ELSIF U.isLetter(c) OR (c = "_") OR (c = "'") OR (c = '"') THEN + k := i; + IF (c = "'") OR (c = '"') THEN + String(text, line, i, y, backColor); + ELSE + ident(text, i, i - ORD((lang = Lang.langC) & (i > 0) & (getChar(line, i - 1) = "#")), y, line, backColor, Lang.isCS(lang)) + END; + IF lang = Lang.langJSON THEN + WHILE Lines.isSpace(getChar(line, i + 1)) DO + INC(i) + END; + IF getChar(line, i + 1) = ":" THEN + PrintLex(text, line, k, i, y, colors.key1, backColor) + END + END; cond := 0 ELSIF U.isDigit(c) THEN k := i; @@ -805,7 +830,7 @@ VAR i: INTEGER; BEGIN i := 0; - WHILE getChar(line, i) = SPACE DO + WHILE Lines.isSpace(getChar(line, i)) DO INC(i) END RETURN i @@ -926,8 +951,9 @@ END getLine; PROCEDURE SetPos* (text: tText; x, y: INTEGER); VAR - deltaY: INTEGER; + deltaY, n, L, R: INTEGER; cursor: pPoint; + c: WCHAR; (* trimLength: INTEGER; *) BEGIN cursor := text.cursor; @@ -949,6 +975,24 @@ BEGIN END END; cursor.X := MIN(MAX(x, 0), text.curLine.length); + c := getChar(text.curLine, cursor.X); + IF c = TAB1 THEN + n := cursor.X; + WHILE getChar(text.curLine, n) = TAB1 DO + INC(n) + END; + R := n - cursor.X; + n := cursor.X; + WHILE getChar(text.curLine, n) # TAB DO + DEC(n) + END; + L := cursor.X - n; + IF L < R THEN + DEC(cursor.X, L) + ELSE + INC(cursor.X, R) + END + END; IF text.scroll.Y > cursor.Y THEN text.scroll.Y := cursor.Y ELSIF text.scroll.Y + textsize.Y <= cursor.Y THEN @@ -965,7 +1009,6 @@ BEGIN setSelect(text); text.foundSel := 0; ShowCursor; - drawCursor := TRUE; text.CurX := -1 END SetPos; @@ -1022,7 +1065,7 @@ END delSelect; PROCEDURE delete (text: tText); VAR - i: INTEGER; + i, n: INTEGER; nextLine, curLine: tLine; BEGIN IF selected(text) THEN @@ -1031,16 +1074,23 @@ BEGIN i := text.cursor.X; curLine := text.curLine; IF i < curLine.length THEN - Lines.delChar(curLine, i); + n := i; + INC(i); + IF getChar(curLine, i - 1) = TAB THEN + WHILE getChar(curLine, i) = TAB1 DO + INC(i) + END + END; + Lines.delCharN(curLine, n, i - n); Lines.modify(curLine); modify(text) ELSE nextLine := curLine.next(tLine); IF nextLine # NIL THEN - Lines.modify(curLine); - modify(text); Lines.insert2(curLine, i, nextLine); - DelLine(text, nextLine) + DelLine(text, nextLine); + Lines.modify(curLine); + modify(text) END END END; @@ -1048,43 +1098,60 @@ BEGIN END delete; +PROCEDURE move (text: tText; d: INTEGER); +VAR + pos: INTEGER; +BEGIN + pos := text.cursor.X + d; + WHILE getChar(text.curLine, pos) = TAB1 DO + INC(pos, d) + END; + SetPos(text, pos, text.cursor.Y) +END move; + + PROCEDURE BkSpace (text: tText); VAR - i, n, k: INTEGER; - curLine, line: tLine; + i, k, n: INTEGER; + curLine, line, line2: tLine; BEGIN IF selected(text) THEN delSelect(text) ELSE resetSelect(text); - i := text.cursor.X; curLine := text.curLine; - IF i > 0 THEN - modify(text); + IF text.cursor.X > 0 THEN + i := text.cursor.X; n := leadingSpaces(curLine); IF n < i THEN - Lines.delChar(curLine, i - 1); - Lines.modify(curLine); - k := 1 + modify(text); + move(text, -1); + delete(text) ELSE n := i; line := curLine.prev(tLine); + line2 := line; k := n; WHILE (line # NIL) & (k >= n) DO IF Lines.trimLength(line) # 0 THEN - k := leadingSpaces(line) + k := leadingSpaces(line); + line2 := line; END; PrevLine(line) END; IF k >= n THEN k := 0 END; - DEC(n, k); - k := n; + n := k; + Lines.delCharN(curLine, 0, i); + Lines.insert3(curLine, 0, k); + WHILE k > 0 DO + Lines.setChar(curLine, k - 1, getChar(line2, k - 1)); + DEC(k) + END; Lines.modify(curLine); - Lines.delCharN(curLine, 0, n) - END; - SetPos(text, text.cursor.X - k, text.cursor.Y) + SetPos(text, n, text.cursor.Y) + END ELSE PrevLine(curLine); IF curLine # NIL THEN @@ -1100,16 +1167,15 @@ END BkSpace; PROCEDURE enter (text: tText); VAR n: INTEGER; - curLine, newLine, line: tLine; + curLine, newLine, line, line2: tLine; BEGIN delSelect(text); newLine := Lines.create(FALSE); - Lines.modify(newLine); modify(text); curLine := text.curLine; IF text.cursor.X < curLine.length THEN - Lines.modify(curLine); - Lines.wrap(curLine, newLine, text.cursor.X) + Lines.wrap(curLine, newLine, text.cursor.X); + Lines.modify(curLine) END; List._insert(text, curLine, newLine); SetPos(text, 0, text.cursor.Y + 1); @@ -1117,7 +1183,8 @@ BEGIN n := -1; WHILE (line # NIL) & (n = -1) DO IF (*line.length*)Lines.trimLength(line) # 0 THEN - n := leadingSpaces(line) + n := leadingSpaces(line); + line2 := line END; PrevLine(line) END; @@ -1128,35 +1195,113 @@ BEGIN SetPos(text, n, text.cursor.Y); resetSelect(text); WHILE n > 0 DO - Lines.setChar(text.curLine, n - 1, SPACE); + Lines.setChar(text.curLine, n - 1, getChar(line2, n - 1)); DEC(n) - END + END; + Lines.modify(newLine) END enter; +PROCEDURE incIndent (line: tLine); +VAR + c: WCHAR; + i: INTEGER; +BEGIN + Lines.modify(line); + Lines.insert3(line, 0, Lines.tab); + IF Lines.tabs THEN + c := TAB1 + ELSE + c := SPACE + END; + i := Lines.tab - 1; + WHILE i >= 0 DO + Lines.setChar(line, i, c); + DEC(i) + END; + IF Lines.tabs THEN + Lines.setChar(line, 0, TAB) + END +END incIndent; + + +PROCEDURE decIndent (line: tLine): BOOLEAN; +VAR + n: INTEGER; +BEGIN + n := leadingSpaces(line); + IF n > 0 THEN + Lines.delCharN(line, 0, MIN(Lines.tab, n)); + Lines.modify(line) + END + RETURN n > 0 +END decIndent; + + +PROCEDURE Indent* (text: tText; incr: BOOLEAN); +VAR + i: INTEGER; + line: tLine; + selBeg, selEnd: tPoint; + modified: BOOLEAN; +BEGIN + getSelect(text, selBeg, selEnd); + i := selEnd.Y - selBeg.Y + 1; + line := getLine(text, selBeg.Y); + modified := incr; + WHILE i > 0 DO + IF incr THEN + incIndent(line) + ELSE + modified := decIndent(line) OR modified + END; + NextLine(line); + DEC(i) + END; + line := getLine(text, selEnd.Y); + text.select^ := selBeg; + text.select.X := 0; + SetPos(text, line.length, selEnd.Y); + IF modified THEN + modify(text) + END +END Indent; + + PROCEDURE input* (text: tText; code: INTEGER); VAR curLine: tLine; + PROCEDURE tab (text: tText); VAR i, x: INTEGER; curLine: tLine; + c: WCHAR; BEGIN - delSelect(text); - curLine := text.curLine; - x := text.cursor.X; - Lines.modify(curLine); - modify(text); - i := TAB - x MOD TAB; - Lines.insert3(curLine, x, i); - SetPos(text, x + i, text.cursor.Y); - WHILE i > 0 DO - Lines.setChar(curLine, x + i - 1, SPACE); - DEC(i) - END + delSelect(text); + curLine := text.curLine; + x := text.cursor.X; + i := Lines.tab - x MOD Lines.tab; + Lines.insert3(curLine, x, i); + SetPos(text, x + i, text.cursor.Y); + IF Lines.tabs THEN + c := TAB1 + ELSE + c := SPACE + END; + WHILE i > 0 DO + Lines.setChar(curLine, x + i - 1, c); + DEC(i) + END; + IF Lines.tabs THEN + Lines.setChar(curLine, x + i, TAB) + END; + Lines.modify(curLine); + modify(text) END tab; + BEGIN IF (code >= ORD(SPACE)) & (code # 127) THEN delSelect(text); @@ -1166,9 +1311,17 @@ BEGIN modify(text); SetPos(text, text.cursor.X + 1, text.cursor.Y) ELSIF code = 8 THEN - BkSpace(text) + BkSpace(text) + ELSIF code = -8 THEN + IF selected(text) THEN + Indent(text, FALSE) + END ELSIF code = 9 THEN - tab(text) + IF selected(text) THEN + Indent(text, TRUE) + ELSE + tab(text) + END ELSIF code = 13 THEN enter(text) END @@ -1276,7 +1429,8 @@ BEGIN END; redoGuard(text, guard); ChangeLog.setGuard(guard); - text.modified := ~guard.saved + text.modified := ~guard.saved; + ShowCursor END undo; @@ -1296,7 +1450,8 @@ BEGIN redoGuard(text, guard) END; ChangeLog.setGuard(guard); - text.modified := ~guard.saved + text.modified := ~guard.saved; + ShowCursor END redo; @@ -1361,34 +1516,56 @@ END copy; PROCEDURE paste (text: tText); VAR line, newLine, curLine: tLine; - L: INTEGER; + w: INTEGER; cliptext: RW.tInput; eol: BOOLEAN; cursor: pPoint; + + + PROCEDURE lineWidth (line: tLine; pos: INTEGER): INTEGER; + VAR + i, res: INTEGER; + c: WCHAR; + BEGIN + res := pos; + i := 0; + REPEAT + c := getChar(line, i); + IF c = TAB THEN + INC(res, Lines.tab - res MOD Lines.tab) + ELSIF c # TAB1 THEN + INC(res) + END; + INC(i) + UNTIL c = 0X + RETURN res - pos - 1 + END lineWidth; + + BEGIN line := Lines.create(TRUE); cliptext := RW.clipboard(); delSelect(text); cursor := text.cursor; - WHILE (cliptext # NIL) & (RW.getString(cliptext, line, eol) >= 0) DO - L := line.length; - IF L > 0 THEN + WHILE (cliptext # NIL) & (RW.getString(cliptext, line, Lines.tabs, eol) >= 0) DO + IF line.length > 0 THEN + w := lineWidth(line, cursor.X); Lines.insert2(text.curLine, cursor.X, line); Lines.modify(text.curLine); modify(text); - SetPos(text, cursor.X + L, cursor.Y); + SetPos(text, cursor.X + w, cursor.Y); resetSelect(text) END; IF eol THEN newLine := Lines.create(FALSE); - Lines.modify(newLine); modify(text); curLine := text.curLine; IF cursor.X < curLine.length THEN - Lines.modify(curLine); - Lines.wrap(curLine, newLine, cursor.X) + Lines.wrap(curLine, newLine, cursor.X); + Lines.modify(curLine) END; List._insert(text, curLine, newLine); + Lines.modify(newLine); SetPos(text, 0, cursor.Y + 1); resetSelect(text) END; @@ -1566,17 +1743,17 @@ BEGIN END delLine; -PROCEDURE dupLine (text: tText); +PROCEDURE dupLine* (text: tText); VAR newLine, curLine: tLine; BEGIN curLine := text.curLine; newLine := Lines.create(FALSE); - Lines.modify(newLine); modify(text); Lines.insert3(newLine, 0, curLine.length); List._insert(text, curLine, newLine); - Lines.move(curLine, newLine) + Lines.move(curLine, newLine); + Lines.modify(newLine) END dupLine; @@ -1589,8 +1766,9 @@ BEGIN END exchange; -PROCEDURE upLine (text: tText); +PROCEDURE upLine* (text: tText); BEGIN + resetSelect(text); IF text.cursor.Y > 0 THEN DEC(text.cursor.Y); exchange(text, text.curLine.prev(tLine), text.curLine) @@ -1598,8 +1776,9 @@ BEGIN END upLine; -PROCEDURE downLine (text: tText); +PROCEDURE downLine* (text: tText); BEGIN + resetSelect(text); IF text.cursor.Y < text.count - 1 THEN INC(text.cursor.Y); exchange(text, text.curLine, text.curLine.next(tLine)) @@ -1644,6 +1823,8 @@ END wordSel; PROCEDURE key* (text: tText; code: INTEGER; shift, ctrl: BOOLEAN); +VAR + n: INTEGER; BEGIN IF shift THEN setSelect(text) @@ -1678,13 +1859,18 @@ BEGIN IF ctrl THEN SetPos(text, 0, 0) ELSE - SetPos(text, 0, text.cursor.Y) + n := leadingSpaces(text.curLine); + IF text.cursor.X > n THEN + SetPos(text, n, text.cursor.Y) + ELSE + SetPos(text, 0, text.cursor.Y) + END END |37: IF (text.cursor.X = 0) & (text.curLine.prev # NIL) THEN SetPos(text, text.curLine.prev(tLine).length, text.cursor.Y - 1) ELSE - SetPos(text, text.cursor.X - 1, text.cursor.Y) + move(text, -1) END |38: IF ctrl THEN @@ -1696,7 +1882,7 @@ BEGIN IF (text.cursor.X = text.curLine.length) & (text.curLine.next # NIL) THEN SetPos(text, 0, text.cursor.Y + 1) ELSE - SetPos(text, text.cursor.X + 1, text.cursor.Y) + move(text, 1) END |40: IF ctrl THEN @@ -1708,7 +1894,8 @@ BEGIN IF ctrl THEN delLine(text) ELSE - delete(text); ShowCursor; drawCursor := TRUE + delete(text); + ShowCursor END |ORD("C"): IF ctrl THEN @@ -1738,7 +1925,12 @@ BEGIN END |ORD("L"), ORD("U"): IF ctrl THEN - changeCase(text, code = ORD("U")) + IF selected(text) THEN + chCase(text, code = ORD("U")) + ELSE + changeCase(text, code = ORD("U")) + END; + ShowCursor END |ORD("D"): IF ctrl THEN @@ -2124,7 +2316,7 @@ BEGIN text.enc := enc; REPEAT line := Lines.create(FALSE); - n := RW.getString(file, line, eol); + n := RW.getString(file, line, Lines.tabs, eol); IF n >= 0 THEN List._append(text, line) ELSE @@ -2291,16 +2483,9 @@ BEGIN END New; -PROCEDURE empty; -END empty; - - PROCEDURE init* (pShowCursor: tProcedure); BEGIN - ShowCursor := empty; - IF pShowCursor # NIL THEN - ShowCursor := pShowCursor - END; + ShowCursor := pShowCursor; pdelete := delete; drawCursor := TRUE; padding.left := pad_left; diff --git a/programs/develop/cedit/SRC/Timer.ob07 b/programs/develop/cedit/SRC/Timer.ob07 new file mode 100644 index 0000000000..c17345ce5f --- /dev/null +++ b/programs/develop/cedit/SRC/Timer.ob07 @@ -0,0 +1,85 @@ +(* + Copyright 2021 Anton Krotov + + This file is part of CEdit. + + CEdit is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + CEdit 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 CEdit. If not, see . +*) + +MODULE Timer; + +IMPORT SYSTEM, K := KolibriOS, KOSAPI, Ini; + +VAR + stack: ARRAY 1024*64 OF INTEGER; + ID*, time, cnt: INTEGER; + paused: BOOLEAN; + + +PROCEDURE reset*; +BEGIN + cnt := time; + paused := FALSE +END reset; + + +PROCEDURE stop*; +BEGIN + cnt := time; + paused := TRUE +END stop; + + +PROCEDURE kill*; +BEGIN + ID := 0; + K.ExitID(ID) +END kill; + + +PROCEDURE [stdcall] main (mainTID: INTEGER); +CONST + step = 5; +BEGIN + WHILE TRUE DO + K.Pause(step); + IF KOSAPI.sysfunc3(18, 21, mainTID) = 0 THEN + ID := 0; + K.Exit + END; + IF ~paused THEN + DEC(cnt, step); + IF cnt <= 0 THEN + cnt := time; + IF time > 0 THEN + K.SendIPC(mainTID, ID) + END + END + END + END +END main; + + +PROCEDURE create* (mainTID: INTEGER); +BEGIN + time := Ini.blink; + reset; + stack[LEN(stack) - 1] := mainTID; + ID := K.CreateThread(SYSTEM.ADR(main), stack) +END create; + + +BEGIN + ID := 0 +END Timer. \ No newline at end of file diff --git a/programs/develop/cedit/SRC/libimg.ob07 b/programs/develop/cedit/SRC/libimg.ob07 index 14784d20dc..19272028e6 100644 --- a/programs/develop/cedit/SRC/libimg.ob07 +++ b/programs/develop/cedit/SRC/libimg.ob07 @@ -1,4 +1,4 @@ -(* +(* Copyright 2016, 2021 Anton Krotov This file is part of CEdit. diff --git a/programs/develop/cedit/SRC/scroll.ob07 b/programs/develop/cedit/SRC/scroll.ob07 index f02ade3083..1246f6288e 100644 --- a/programs/develop/cedit/SRC/scroll.ob07 +++ b/programs/develop/cedit/SRC/scroll.ob07 @@ -21,123 +21,339 @@ MODULE Scroll; IMPORT G := Graph, K := KolibriOS; +CONST + + ScrollIPC* = 0; + TYPE - tScroll* = POINTER TO RECORD - vertical, mouse: BOOLEAN; - canvas: G.tCanvas; - xSize*, ySize*, pos, mousePos: INTEGER; - value*, maxVal*: INTEGER - END; + tScroll* = RECORD + vertical, Inc*, Dec*, mouse*: BOOLEAN; + top*, left*, + width*, height*: INTEGER; (* read only *) + btnSize, sliderSize: INTEGER; + pos, Slider, pos0, maxVal*, value*: INTEGER; + canvas*: G.tCanvas + END; -PROCEDURE draw* (scroll: tScroll; x, y: INTEGER); -VAR - pos, a, b: INTEGER; - canvas: G.tCanvas; +PROCEDURE create* (vertical: BOOLEAN; width, height: INTEGER; btnSize, sliderSize: INTEGER; VAR scroll: tScroll); BEGIN - IF scroll.vertical THEN - a := scroll.ySize; - b := scroll.xSize - ELSE - a := scroll.xSize; - b := scroll.ySize - END; - IF scroll.maxVal > 0 THEN - pos := (a - b)*scroll.value DIV scroll.maxVal - ELSE - pos := 0 - END; - canvas := scroll.canvas; - G.SetColor(canvas, K.scrollBkColor); - G.clear(canvas); - G.SetColor(canvas, K.borderColor); - G.Rect(canvas, 0, 0, scroll.xSize - 1, scroll.ySize - 1); - G.SetColor(canvas, K.scrollColor); - DEC(b, 2); - IF scroll.vertical THEN - G.FillRect(canvas, 1, pos + 1, b, pos + b); - G.SetColor(canvas, K.borderColor); - G.Rect(canvas, 0, pos, b + 2, pos + b + 1); - G.SetColor(canvas, K.btnTextColor); - G.HLine(canvas, pos + 1 + b DIV 2, 4, b - 4); - G.HLine(canvas, pos + 1 + b DIV 2 - 3, 6, b - 6); - G.HLine(canvas, pos + 1 + b DIV 2 + 3, 6, b - 6); - ELSE - G.FillRect(canvas, pos + 1, 1, pos + b, b); - G.SetColor(canvas, K.borderColor); - G.Rect(canvas, pos, 0, pos + b + 1, b + 2); - G.SetColor(canvas, K.btnTextColor); - G.VLine(canvas, pos + b DIV 2, 4, b - 4); - G.VLine(canvas, pos + b DIV 2 - 3, 6, b - 6); - G.VLine(canvas, pos + b DIV 2 + 3, 6, b - 6); - END; - scroll.pos := pos; - G.DrawCanvas(canvas, x, y); -END draw; - - -PROCEDURE create* (xSize, ySize: INTEGER): tScroll; -VAR - scroll: tScroll; -BEGIN - NEW(scroll); - scroll.xSize := xSize; - scroll.ySize := ySize; - scroll.vertical := xSize < ySize; - scroll.maxVal := 30; - scroll.value := 0; - scroll.mouse := FALSE; - scroll.canvas := G.CreateCanvas(xSize, ySize) - RETURN scroll + scroll.vertical := vertical; + scroll.Inc := FALSE; + scroll.Dec := FALSE; + scroll.Slider := -1; + scroll.mouse := FALSE; + scroll.left := 0; + scroll.top := 0; + scroll.width := width; + scroll.height := height; + scroll.btnSize := btnSize; + scroll.sliderSize := sliderSize; + scroll.pos := 0; + scroll.maxVal := 0; + scroll.canvas := G.CreateCanvas(width, height) END create; -PROCEDURE resize* (scroll: tScroll; xSize, ySize: INTEGER); +PROCEDURE Rect (canvas: G.tCanvas; left, top, right, bottom: INTEGER); BEGIN - scroll.xSize := xSize; - scroll.ySize := ySize; - scroll.vertical := xSize < ySize; - G.destroy(scroll.canvas); - scroll.canvas := G.CreateCanvas(xSize, ySize); + G.FillRect(canvas, left, top, right, bottom); + G.SetColor(canvas, K.borderColor); + G.Rect(canvas, left, top, right, bottom); +END Rect; + + +PROCEDURE _paint (scroll: tScroll); +VAR + canvas: G.tCanvas; + x, y, d, x1, x2, y1, y2, + width, height, btn: INTEGER; + + + PROCEDURE SetColor (canvas: G.tCanvas; c: BOOLEAN); + VAR + color: INTEGER; + BEGIN + IF c THEN + color := K.btnColor + ELSE + color := K.btnTextColor + END; + G.SetColor(canvas, color) + END SetColor; + + +BEGIN + btn := scroll.btnSize; + width := scroll.width; + height := scroll.height; + canvas := scroll.canvas; + G.SetColor(canvas, K.winColor); + G.FillRect(canvas, 0, 0, width - 1, height - 1); + G.SetColor(canvas, K.borderColor); + G.Rect(canvas, 0, 0, width - 1, height - 1); + IF scroll.vertical THEN + SetColor(canvas, ~scroll.Dec); + Rect(canvas, 0, 0, width - 1, btn - 1); + SetColor(canvas, ~scroll.Inc); + Rect(canvas, 0, height - btn, width - 1, height - 1); + G.SetColor(canvas, K.btnColor); + Rect(canvas, 0, btn + scroll.pos - 1, width - 1, btn + scroll.pos + scroll.sliderSize - 1); + + G.SetColor(canvas, K.btnTextColor); + + y := btn + scroll.pos + scroll.sliderSize DIV 2 - 1; + G.HLine(canvas, y, width DIV 4, 3*width DIV 4); + G.HLine(canvas, y - 3, width DIV 3, 2*width DIV 3); + G.HLine(canvas, y + 3, width DIV 3, 2*width DIV 3); + + d := 4*width DIV 10; + x1 := (width - d) DIV 2; + x2 := x1 + d; + + SetColor(canvas, scroll.Dec); + y := (btn - d DIV 2) DIV 2 + d DIV 2 - 1; + G.Triangle(canvas, x1 - 1, y, x2, y, G.triUp); + + SetColor(canvas, scroll.Inc); + y := y + height - btn - d DIV 2 + 1; + G.Triangle(canvas, x1 - 1, y, x2, y, G.triDown); + ELSE + SetColor(canvas, ~scroll.Dec); + Rect(canvas, 0, 0, btn - 1, height - 1); + SetColor(canvas, ~scroll.Inc); + Rect(canvas, width - btn, 0, width - 1, height - 1); + G.SetColor(canvas, K.btnColor); + Rect(canvas, btn + scroll.pos - 1, 0, btn + scroll.pos + scroll.sliderSize - 1, height - 1); + + G.SetColor(canvas, K.btnTextColor); + + x := btn + scroll.pos + scroll.sliderSize DIV 2 - 1; + G.VLine(canvas, x, height DIV 4, 3*height DIV 4); + G.VLine(canvas, x - 3, height DIV 3, 2*height DIV 3); + G.VLine(canvas, x + 3, height DIV 3, 2*height DIV 3); + + d := 4*height DIV 10; + y1 := (height - d) DIV 2; + y2 := y1 + d; + + SetColor(canvas, scroll.Dec); + x := (btn - d DIV 2) DIV 2 + d DIV 2 - 1; + G.Triangle(canvas, x, y1 - 1, x, y2, G.triLeft); + + SetColor(canvas, scroll.Inc); + x := x + width - btn - d DIV 2 + 1; + G.Triangle(canvas, x, y1 - 1, x, y2, G.triRight); + END; + G.DrawCanvas(scroll.canvas, scroll.left, scroll.top) +END _paint; + + +PROCEDURE paint* (scroll: tScroll); +BEGIN + IF scroll.canvas # NIL THEN + _paint(scroll) + END +END paint; + + +PROCEDURE resize* (VAR scroll: tScroll; width, height: INTEGER); +BEGIN + G.destroy(scroll.canvas); + scroll.canvas := G.CreateCanvas(width, height); + scroll.width := width; + scroll.height := height; + paint(scroll) END resize; -PROCEDURE mouse* (scroll: tScroll; x, y: INTEGER); +PROCEDURE setValue* (VAR scroll: tScroll; value: INTEGER); VAR - pos, b: INTEGER; + pos, maxPos, n, m: INTEGER; BEGIN - IF scroll.vertical THEN - pos := y - 1; - b := scroll.xSize - 2 - ELSE - pos := x - 1; - b := scroll.ySize - 2 - END; - IF ~scroll.mouse THEN - scroll.mouse := TRUE; - IF (scroll.pos <= pos) & (pos <= scroll.pos + b - 1) THEN - scroll.mousePos := pos - scroll.pos - ELSE - scroll.mousePos := b DIV 2; - scroll.value := (pos - scroll.mousePos)*scroll.maxVal DIV ABS(scroll.xSize - scroll.ySize) - END - ELSE - scroll.value := (pos - scroll.mousePos)*scroll.maxVal DIV ABS(scroll.xSize - scroll.ySize) - END; - IF scroll.value < 0 THEN - scroll.value := 0 - ELSIF scroll.value > scroll.maxVal THEN - scroll.value := scroll.maxVal - END -END mouse; + IF scroll.vertical THEN + maxPos := scroll.height + ELSE + maxPos := scroll.width + END; + maxPos := maxPos - scroll.btnSize*2 - scroll.sliderSize + 1; + IF (value < 0) OR (scroll.maxVal <= 0) THEN + value := 0; + pos := 0 + ELSIF value > scroll.maxVal THEN + value := scroll.maxVal; + pos := maxPos + ELSE + IF (maxPos + 1) >= scroll.maxVal THEN + n := (maxPos + 1) DIV scroll.maxVal; + m := (maxPos + 1) MOD scroll.maxVal; + pos := value*n + MIN(value, m) + ELSE + pos := value*(maxPos + 1) DIV scroll.maxVal + END; + IF pos > maxPos THEN + pos := maxPos; + value := scroll.maxVal + END + END; + scroll.pos := pos; + scroll.value := value +END setValue; -PROCEDURE MouseUp* (scroll: tScroll); +PROCEDURE change* (VAR scroll: tScroll); BEGIN - IF scroll # NIL THEN - scroll.mouse := FALSE - END + IF scroll.Inc THEN + setValue(scroll, scroll.value + 1) + ELSIF scroll.Dec THEN + setValue(scroll, scroll.value - 1) + END; + paint(scroll) +END change; + + +PROCEDURE ceil (p, q: INTEGER): INTEGER; + RETURN p DIV q + ORD(p MOD q # 0) +END ceil; + + +PROCEDURE setPos (VAR scroll: tScroll; pos: INTEGER); +VAR + maxPos, value, n, m, x, x0, q: INTEGER; +BEGIN + IF scroll.maxVal > 0 THEN + IF scroll.vertical THEN + maxPos := scroll.height + ELSE + maxPos := scroll.width + END; + maxPos := maxPos - scroll.btnSize*2 - scroll.sliderSize + 1; + IF pos <= 0 THEN + pos := 0; + value := 0 + ELSIF pos >= maxPos THEN + pos := maxPos; + value := scroll.maxVal + ELSE + IF scroll.maxVal <= maxPos + 1 THEN + n := (maxPos + 1) DIV scroll.maxVal; + m := (maxPos + 1) MOD scroll.maxVal; + + q := m*(n + 1); + IF q < pos THEN + value := ceil(pos - m, n) + ELSIF q > pos THEN + value := ceil(pos, n + 1) + ELSE + value := m + END; + + x := value*n + MIN(value, m); + x0 := (value - 1)*n + MIN(value - 1, m); + + IF x - pos > pos - x0 THEN + pos := x0; + DEC(value) + ELSE + pos := x; + IF pos > maxPos THEN + pos := maxPos; + value := scroll.maxVal + END + END + ELSE + value := scroll.maxVal*pos DIV (maxPos + 1) + END + END + ELSE + pos := 0; + scroll.value := 0 + END; + scroll.pos := pos; + scroll.value := value +END setPos; + + +PROCEDURE isActive* (scroll: tScroll): BOOLEAN; + RETURN scroll.Inc OR scroll.Dec OR (scroll.Slider # -1) +END isActive; + + +PROCEDURE MouseMove* (VAR scroll: tScroll; x, y: INTEGER); +VAR + c: INTEGER; +BEGIN + IF scroll.Slider # -1 THEN + IF scroll.vertical THEN + c := y - scroll.top + ELSE + c := x - scroll.left + END; + setPos(scroll, scroll.pos0 + c - scroll.Slider); + paint(scroll) + END +END MouseMove; + + +PROCEDURE between (a, b, c: INTEGER): BOOLEAN; + RETURN (a <= b) & (b <= c) +END between; + + +PROCEDURE SendIPC*; +BEGIN + K.SendIPC(K.ThreadID(), ScrollIPC) +END SendIPC; + + +PROCEDURE MouseDown* (VAR scroll: tScroll; x, y: INTEGER); +VAR + c, size: INTEGER; +BEGIN + x := x - scroll.left; + y := y - scroll.top; + scroll.mouse := TRUE; + IF between(0, x, scroll.width - 1) & between(0, y, scroll.height - 1) THEN + IF scroll.vertical THEN + c := y; + size := scroll.height + ELSE + c := x; + size := scroll.width + END; + IF between(scroll.btnSize + scroll.pos - 1, c, scroll.btnSize + scroll.pos + scroll.sliderSize - 1) THEN + scroll.pos0 := scroll.pos; + scroll.Slider := c + ELSE + IF between(0, c, scroll.btnSize - 1) THEN + scroll.Dec := TRUE; + SendIPC + ELSE + IF between(size - scroll.btnSize, c, size - 1) THEN + scroll.Inc := TRUE; + SendIPC + ELSE + setPos(scroll, c - scroll.btnSize - scroll.sliderSize DIV 2); + scroll.pos0 := scroll.pos; + scroll.Slider := c; + paint(scroll) + END + END + END + END +END MouseDown; + + +PROCEDURE MouseUp* (VAR scroll: tScroll); +BEGIN + IF scroll.mouse THEN + scroll.Slider := -1; + scroll.Inc := FALSE; + scroll.Dec := FALSE; + scroll.mouse := FALSE; + paint(scroll) + END END MouseUp;