text.b 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455
  1. implement Textm;
  2. include "common.m";
  3. include "keyboard.m";
  4. sys : Sys;
  5. utils : Utils;
  6. framem : Framem;
  7. drawm : Draw;
  8. acme : Acme;
  9. graph : Graph;
  10. gui : Gui;
  11. dat : Dat;
  12. scrl : Scroll;
  13. bufferm : Bufferm;
  14. filem : Filem;
  15. columnm : Columnm;
  16. windowm : Windowm;
  17. exec : Exec;
  18. Dir, sprint : import sys;
  19. frgetmouse : import acme;
  20. min, warning, error, stralloc, strfree, isalnum : import utils;
  21. Frame, frinsert, frdelete, frptofchar, frcharofpt, frselect, frdrawsel, frdrawsel0, frtick : import framem;
  22. BUFSIZE, Astring, SZINT, TRUE, FALSE, XXX, Reffont, Dirlist,Scrollwid, Scrollgap, seq, mouse : import dat;
  23. EM_NORMAL, EM_RAW, EM_MASK : import dat;
  24. ALPHA_LATIN, ALPHA_GREEK, ALPHA_CYRILLIC: import Dat;
  25. BACK, TEXT, HIGH, HTEXT : import Framem;
  26. Flushon, Flushoff : import Draw;
  27. Point, Display, Rect, Image : import drawm;
  28. charwidth, bflush, draw : import graph;
  29. black, white, mainwin, display : import gui;
  30. Buffer : import bufferm;
  31. File : import filem;
  32. Column : import columnm;
  33. Window : import windowm;
  34. scrdraw : import scrl;
  35. cvlist: adt {
  36. ld: int;
  37. nm: string;
  38. si: string;
  39. so: string;
  40. };
  41. # "@@", "'EKSTYZekstyz ", "ьЕКСТЫЗекстызъЁё",
  42. latintab := array[] of {
  43. cvlist(
  44. ALPHA_LATIN,
  45. "latin",
  46. nil,
  47. nil
  48. ),
  49. cvlist(
  50. ALPHA_GREEK,
  51. "greek",
  52. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
  53. "ΑΒΞΔΕΦΓΘΙΪΚΛΜΝΟΠΨΡΣΤΥΫΩΧΗΖαβξδεφγθιϊκλμνοπψρστυϋωχηζ"
  54. ),
  55. cvlist(
  56. ALPHA_CYRILLIC,
  57. "cyrillic",
  58. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
  59. "АБЧДЭФГШИЙХЛМНОПЕРЩЦУВЮХЯЖабчдэфгшийхлмноперщцувюхяж"
  60. ),
  61. cvlist(-1, nil, nil, nil)
  62. };
  63. alphabet := ALPHA_LATIN; # per window perhaps
  64. setalphabet(s: string)
  65. {
  66. for(a := 0; latintab[a].ld != -1; a++){
  67. k := latintab[a].ld;
  68. for(i := 0; latintab[i].ld != -1; i++){
  69. if(s == transs(latintab[i].nm, k)){
  70. alphabet = latintab[i].ld;
  71. return;
  72. }
  73. }
  74. }
  75. }
  76. transc(c: int, k: int): int
  77. {
  78. for(i := 0; latintab[i].ld != -1; i++){
  79. if(k == latintab[i].ld){
  80. si := latintab[i].si;
  81. so := latintab[i].so;
  82. ln := len si;
  83. for(j := 0; j < ln; j++)
  84. if(c == si[j])
  85. return so[j];
  86. }
  87. }
  88. return c;
  89. }
  90. transs(s: string, k: int): string
  91. {
  92. ln := len s;
  93. for(i := 0; i < ln; i++)
  94. s[i] = transc(s[i], k);
  95. return s;
  96. }
  97. init(mods : ref Dat->Mods)
  98. {
  99. sys = mods.sys;
  100. framem = mods.framem;
  101. dat = mods.dat;
  102. utils = mods.utils;
  103. drawm = mods.draw;
  104. acme = mods.acme;
  105. graph = mods.graph;
  106. gui = mods.gui;
  107. scrl = mods.scroll;
  108. bufferm = mods.bufferm;
  109. filem = mods.filem;
  110. columnm = mods.columnm;
  111. windowm = mods.windowm;
  112. exec = mods.exec;
  113. }
  114. TABDIR : con 3; # width of tabs in directory windows
  115. # remove eventually
  116. KF : con 16rF000;
  117. Kup : con KF | 16r0E;
  118. Kleft : con KF | 16r11;
  119. Kright : con KF | 16r12;
  120. Kend : con KF | 16r18;
  121. Kdown : con 16r80;
  122. nulltext : Text;
  123. newtext() : ref Text
  124. {
  125. t := ref nulltext;
  126. t.frame = framem->newframe();
  127. return t;
  128. }
  129. Text.init(t : self ref Text, f : ref File, r : Rect, rf : ref Dat->Reffont, cols : array of ref Image)
  130. {
  131. t.file = f;
  132. t.all = r;
  133. t.scrollr = r;
  134. t.scrollr.max.x = r.min.x+Scrollwid;
  135. t.lastsr = dat->nullrect;
  136. r.min.x += Scrollwid+Scrollgap;
  137. t.eq0 = ~0;
  138. t.ncache = 0;
  139. t.reffont = rf;
  140. t.tabstop = dat->maxtab;
  141. for(i:=0; i<Framem->NCOL; i++)
  142. t.frame.cols[i] = cols[i];
  143. t.redraw(r, rf.f, mainwin, -1);
  144. }
  145. Text.redraw(t : self ref Text, r : Rect, f : ref Draw->Font, b : ref Image, odx : int)
  146. {
  147. framem->frinit(t.frame, r, f, b, t.frame.cols);
  148. rr := t.frame.r;
  149. rr.min.x -= Scrollwid; # back fill to scroll bar
  150. draw(t.frame.b, rr, t.frame.cols[Framem->BACK], nil, (0, 0));
  151. # use no wider than 3-space tabs in a directory
  152. maxt := dat->maxtab;
  153. if(t.what == Body){
  154. if(t.w != nil && t.w.isdir)
  155. maxt = min(TABDIR, dat->maxtab);
  156. else
  157. maxt = t.tabstop;
  158. }
  159. t.frame.maxtab = maxt*charwidth(f, '0');
  160. # c = '0';
  161. # if(t.what==Body && t.w!=nil && t.w.isdir)
  162. # c = ' ';
  163. # t.frame.maxtab = Dat->Maxtab*charwidth(f, c);
  164. if(t.what==Body && t.w.isdir && odx!=t.all.dx()){
  165. if(t.frame.maxlines > 0){
  166. t.reset();
  167. t.columnate(t.w.dlp, t.w.ndl);
  168. t.show(0, 0, TRUE);
  169. }
  170. }else{
  171. t.fill();
  172. t.setselect(t.q0, t.q1);
  173. }
  174. }
  175. Text.reshape(t : self ref Text, r : Rect) : int
  176. {
  177. odx : int;
  178. if(r.dy() > 0)
  179. r.max.y -= r.dy()%t.frame.font.height;
  180. else
  181. r.max.y = r.min.y;
  182. odx = t.all.dx();
  183. t.all = r;
  184. t.scrollr = r;
  185. t.scrollr.max.x = r.min.x+Scrollwid;
  186. t.lastsr = dat->nullrect;
  187. r.min.x += Scrollwid+Scrollgap;
  188. framem->frclear(t.frame, 0);
  189. # t.redraw(r, t.frame.font, t.frame.b, odx);
  190. t.redraw(r, t.frame.font, mainwin, odx);
  191. return r.max.y;
  192. }
  193. Text.close(t : self ref Text)
  194. {
  195. t.cache = nil;
  196. framem->frclear(t.frame, 1);
  197. t.file.deltext(t);
  198. t.file = nil;
  199. t.reffont.close();
  200. if(dat->argtext == t)
  201. dat->argtext = nil;
  202. if(dat->typetext == t)
  203. dat->typetext = nil;
  204. if(dat->seltext == t)
  205. dat->seltext = nil;
  206. if(dat->mousetext == t)
  207. dat->mousetext = nil;
  208. if(dat->barttext == t)
  209. dat->barttext = nil;
  210. }
  211. dircmp(da : ref Dirlist, db : ref Dirlist) : int
  212. {
  213. if (da.r < db.r)
  214. return -1;
  215. if (da.r > db.r)
  216. return 1;
  217. return 0;
  218. }
  219. qsort(a : array of ref Dirlist, n : int)
  220. {
  221. i, j : int;
  222. t : ref Dirlist;
  223. while(n > 1) {
  224. i = n>>1;
  225. t = a[0]; a[0] = a[i]; a[i] = t;
  226. i = 0;
  227. j = n;
  228. for(;;) {
  229. do
  230. i++;
  231. while(i < n && dircmp(a[i], a[0]) < 0);
  232. do
  233. j--;
  234. while(j > 0 && dircmp(a[j], a[0]) > 0);
  235. if(j < i)
  236. break;
  237. t = a[i]; a[i] = a[j]; a[j] = t;
  238. }
  239. t = a[0]; a[0] = a[j]; a[j] = t;
  240. n = n-j-1;
  241. if(j >= n) {
  242. qsort(a, j);
  243. a = a[j+1:];
  244. } else {
  245. qsort(a[j+1:], n);
  246. n = j;
  247. }
  248. }
  249. }
  250. Text.columnate(t : self ref Text, dlp : array of ref Dirlist, ndl : int)
  251. {
  252. i, j, w, colw, mint, maxt, ncol, nrow : int;
  253. dl : ref Dirlist;
  254. q1 : int;
  255. if(t.file.ntext > 1)
  256. return;
  257. mint = charwidth(t.frame.font, '0');
  258. # go for narrower tabs if set more than 3 wide
  259. t.frame.maxtab = min(dat->maxtab, TABDIR)*mint;
  260. maxt = t.frame.maxtab;
  261. colw = 0;
  262. for(i=0; i<ndl; i++){
  263. dl = dlp[i];
  264. w = dl.wid;
  265. if(maxt-w%maxt < mint)
  266. w += mint;
  267. if(w % maxt)
  268. w += maxt-(w%maxt);
  269. if(w > colw)
  270. colw = w;
  271. }
  272. if(colw == 0)
  273. ncol = 1;
  274. else
  275. ncol = utils->max(1, t.frame.r.dx()/colw);
  276. nrow = (ndl+ncol-1)/ncol;
  277. q1 = 0;
  278. for(i=0; i<nrow; i++){
  279. for(j=i; j<ndl; j+=nrow){
  280. dl = dlp[j];
  281. t.file.insert(q1, dl.r, len dl.r);
  282. q1 += len dl.r;
  283. if(j+nrow >= ndl)
  284. break;
  285. w = dl.wid;
  286. if(maxt-w%maxt < mint){
  287. t.file.insert(q1, "\t", 1);
  288. q1++;
  289. w += mint;
  290. }
  291. do{
  292. t.file.insert(q1, "\t", 1);
  293. q1++;
  294. w += maxt-(w%maxt);
  295. }while(w < colw);
  296. }
  297. t.file.insert(q1, "\n", 1);
  298. q1++;
  299. }
  300. }
  301. Text.loadx(t : self ref Text, q0 : int, file : string, setqid : int) : int
  302. {
  303. rp : ref Astring;
  304. dl : ref Dirlist;
  305. dlp : array of ref Dirlist;
  306. i, n, ndl : int;
  307. fd : ref Sys->FD;
  308. q, q1 : int;
  309. d : Dir;
  310. u : ref Text;
  311. ok : int;
  312. if(t.ncache!=0 || t.file.buf.nc || t.w==nil || t!=t.w.body || (t.w.isdir && t.file.name==nil))
  313. error("text.load");
  314. {
  315. fd = sys->open(file, Sys->OREAD);
  316. if(fd == nil){
  317. warning(nil, sprint("can't open %s: %r\n", file));
  318. raise "e";
  319. }
  320. (ok, d) = sys->fstat(fd);
  321. if(ok){
  322. warning(nil, sprint("can't fstat %s: %r\n", file));
  323. raise "e";
  324. }
  325. if(d.qid.qtype & Sys->QTDIR){
  326. # this is checked in get() but it's possible the file changed underfoot
  327. if(t.file.ntext > 1){
  328. warning(nil, sprint("%s is a directory; can't read with multiple windows on it\n", file));
  329. raise "e";
  330. }
  331. t.w.isdir = TRUE;
  332. t.w.filemenu = FALSE;
  333. if(t.file.name[len t.file.name-1] != '/')
  334. t.w.setname(t.file.name + "/", len t.file.name+1);
  335. dlp = nil;
  336. ndl = 0;
  337. for(;;){
  338. (nd, dbuf) := sys->dirread(fd);
  339. if(nd <= 0)
  340. break;
  341. for(i=0; i<nd; i++){
  342. dl = ref Dirlist;
  343. dl.r = dbuf[i].name;
  344. if(dbuf[i].mode & Sys->DMDIR)
  345. dl.r = dl.r + "/";
  346. dl.wid = graph->strwidth(t.frame.font, dl.r);
  347. ndl++;
  348. odlp := dlp;
  349. dlp = array[ndl] of ref Dirlist;
  350. dlp[0:] = odlp[0:ndl-1];
  351. odlp = nil;
  352. dlp[ndl-1] = dl;
  353. }
  354. }
  355. qsort(dlp, ndl);
  356. t.w.dlp = dlp;
  357. t.w.ndl = ndl;
  358. t.columnate(dlp, ndl);
  359. q1 = t.file.buf.nc;
  360. }else{
  361. tmp : int;
  362. t.w.isdir = FALSE;
  363. t.w.filemenu = TRUE;
  364. tmp = t.file.loadx(q0, fd);
  365. q1 = q0 + tmp;
  366. }
  367. fd = nil;
  368. if(setqid){
  369. t.file.dev = d.dev;
  370. t.file.mtime = d.mtime;
  371. t.file.qidpath = d.qid.path;
  372. }
  373. rp = stralloc(BUFSIZE);
  374. for(q=q0; q<q1; q+=n){
  375. n = q1-q;
  376. if(n > Dat->BUFSIZE)
  377. n = Dat->BUFSIZE;
  378. t.file.buf.read(q, rp, 0, n);
  379. if(q < t.org)
  380. t.org += n;
  381. else if(q <= t.org+t.frame.nchars)
  382. frinsert(t.frame, rp.s, n, q-t.org);
  383. if(t.frame.lastlinefull)
  384. break;
  385. }
  386. strfree(rp);
  387. rp = nil;
  388. for(i=0; i<t.file.ntext; i++){
  389. u = t.file.text[i];
  390. if(u != t){
  391. if(u.org > u.file.buf.nc) # will be 0 because of reset(), but safety first
  392. u.org = 0;
  393. u.reshape(u.all);
  394. u.backnl(u.org, 0); # go to beginning of line
  395. }
  396. u.setselect(q0, q0);
  397. }
  398. return q1-q0;
  399. }
  400. exception{
  401. * =>
  402. fd = nil;
  403. return 0;
  404. }
  405. return 0;
  406. }
  407. Text.bsinsert(t : self ref Text, q0 : int, r : string, n : int, tofile : int) : (int, int)
  408. {
  409. tp : ref Astring;
  410. bp, up : int;
  411. i, initial : int;
  412. {
  413. if(t.what == Tag) # can't happen but safety first: mustn't backspace over file name
  414. raise "e";
  415. bp = 0;
  416. for(i=0; i<n; i++)
  417. if(r[bp++] == '\b'){
  418. --bp;
  419. initial = 0;
  420. tp = utils->stralloc(n);
  421. for (k := 0; k < i; k++)
  422. tp.s[k] = r[k];
  423. up = i;
  424. for(; i<n; i++){
  425. tp.s[up] = r[bp++];
  426. if(tp.s[up] == '\b')
  427. if(up == 0)
  428. initial++;
  429. else
  430. --up;
  431. else
  432. up++;
  433. }
  434. if(initial){
  435. if(initial > q0)
  436. initial = q0;
  437. q0 -= initial;
  438. t.delete(q0, q0+initial, tofile);
  439. }
  440. n = up;
  441. t.insert(q0, tp.s, n, tofile, 0);
  442. strfree(tp);
  443. tp = nil;
  444. return (q0, n);
  445. }
  446. raise "e";
  447. return(0, 0);
  448. }
  449. exception{
  450. * =>
  451. t.insert(q0, r, n, tofile, 0);
  452. return (q0, n);
  453. }
  454. return (0, 0);
  455. }
  456. Text.insert(t : self ref Text, q0 : int, r : string, n : int, tofile : int, echomode : int)
  457. {
  458. c, i : int;
  459. u : ref Text;
  460. if(tofile && t.ncache != 0)
  461. error("text.insert");
  462. if(n == 0)
  463. return;
  464. if(tofile){
  465. t.file.insert(q0, r, n);
  466. if(t.what == Body){
  467. t.w.dirty = TRUE;
  468. t.w.utflastqid = -1;
  469. }
  470. if(t.file.ntext > 1)
  471. for(i=0; i<t.file.ntext; i++){
  472. u = t.file.text[i];
  473. if(u != t){
  474. u.w.dirty = TRUE; # always a body
  475. u.insert(q0, r, n, FALSE, echomode);
  476. u.setselect(u.q0, u.q1);
  477. scrdraw(u);
  478. }
  479. }
  480. }
  481. if(q0 < t.q1)
  482. t.q1 += n;
  483. if(q0 < t.q0)
  484. t.q0 += n;
  485. if(q0 < t.org)
  486. t.org += n;
  487. else if(q0 <= t.org+t.frame.nchars) {
  488. if (echomode == EM_MASK && len r == 1 && r[0] != '\n')
  489. frinsert(t.frame, "*", n, q0-t.org);
  490. else
  491. frinsert(t.frame, r, n, q0-t.org);
  492. }
  493. if(t.w != nil){
  494. c = 'i';
  495. if(t.what == Body)
  496. c = 'I';
  497. if(n <= Dat->EVENTSIZE)
  498. t.w.event(sprint("%c%d %d 0 %d %s\n", c, q0, q0+n, n, r[0:n]));
  499. else
  500. t.w.event(sprint("%c%d %d 0 0 \n", c, q0, q0+n));
  501. }
  502. }
  503. Text.fill(t : self ref Text)
  504. {
  505. rp : ref Astring;
  506. i, n, m, nl : int;
  507. if(t.frame.lastlinefull || t.nofill)
  508. return;
  509. if(t.ncache > 0){
  510. if(t.w != nil)
  511. t.w.commit(t);
  512. else
  513. t.commit(TRUE);
  514. }
  515. rp = stralloc(BUFSIZE);
  516. do{
  517. n = t.file.buf.nc-(t.org+t.frame.nchars);
  518. if(n == 0)
  519. break;
  520. if(n > 2000) # educated guess at reasonable amount
  521. n = 2000;
  522. t.file.buf.read(t.org+t.frame.nchars, rp, 0, n);
  523. #
  524. # it's expensive to frinsert more than we need, so
  525. # count newlines.
  526. #
  527. nl = t.frame.maxlines-t.frame.nlines;
  528. m = 0;
  529. for(i=0; i<n; ){
  530. if(rp.s[i++] == '\n'){
  531. m++;
  532. if(m >= nl)
  533. break;
  534. }
  535. }
  536. frinsert(t.frame, rp.s, i, t.frame.nchars);
  537. }while(t.frame.lastlinefull == FALSE);
  538. strfree(rp);
  539. rp = nil;
  540. }
  541. Text.delete(t : self ref Text, q0 : int, q1 : int, tofile : int)
  542. {
  543. n, p0, p1 : int;
  544. i, c : int;
  545. u : ref Text;
  546. if(tofile && t.ncache != 0)
  547. error("text.delete");
  548. n = q1-q0;
  549. if(n == 0)
  550. return;
  551. if(tofile){
  552. t.file.delete(q0, q1);
  553. if(t.what == Body){
  554. t.w.dirty = TRUE;
  555. t.w.utflastqid = -1;
  556. }
  557. if(t.file.ntext > 1)
  558. for(i=0; i<t.file.ntext; i++){
  559. u = t.file.text[i];
  560. if(u != t){
  561. u.w.dirty = TRUE; # always a body
  562. u.delete(q0, q1, FALSE);
  563. u.setselect(u.q0, u.q1);
  564. scrdraw(u);
  565. }
  566. }
  567. }
  568. if(q0 < t.q0)
  569. t.q0 -= min(n, t.q0-q0);
  570. if(q0 < t.q1)
  571. t.q1 -= min(n, t.q1-q0);
  572. if(q1 <= t.org)
  573. t.org -= n;
  574. else if(q0 < t.org+t.frame.nchars){
  575. p1 = q1 - t.org;
  576. if(p1 > t.frame.nchars)
  577. p1 = t.frame.nchars;
  578. if(q0 < t.org){
  579. t.org = q0;
  580. p0 = 0;
  581. }else
  582. p0 = q0 - t.org;
  583. frdelete(t.frame, p0, p1);
  584. t.fill();
  585. }
  586. if(t.w != nil){
  587. c = 'd';
  588. if(t.what == Body)
  589. c = 'D';
  590. t.w.event(sprint("%c%d %d 0 0 \n", c, q0, q1));
  591. }
  592. }
  593. onechar : ref Astring;
  594. Text.readc(t : self ref Text, q : int) : int
  595. {
  596. if(t.cq0<=q && q<t.cq0+t.ncache)
  597. return t.cache[q-t.cq0];
  598. if (onechar == nil)
  599. onechar = stralloc(1);
  600. t.file.buf.read(q, onechar, 0, 1);
  601. return onechar.s[0];
  602. }
  603. Text.bswidth(t : self ref Text, c : int) : int
  604. {
  605. q, eq : int;
  606. r : int;
  607. skipping : int;
  608. # there is known to be at least one character to erase
  609. if(c == 16r08) # ^H: erase character
  610. return 1;
  611. q = t.q0;
  612. skipping = TRUE;
  613. while(q > 0){
  614. r = t.readc(q-1);
  615. if(r == '\n'){ # eat at most one more character
  616. if(q == t.q0) # eat the newline
  617. --q;
  618. break;
  619. }
  620. if(c == 16r17){
  621. eq = isalnum(r);
  622. if(eq && skipping) # found one; stop skipping
  623. skipping = FALSE;
  624. else if(!eq && !skipping)
  625. break;
  626. }
  627. --q;
  628. }
  629. return t.q0-q;
  630. }
  631. Text.typex(t : self ref Text, r : int, echomode : int)
  632. {
  633. q0, q1 : int;
  634. nnb, nb, n, i : int;
  635. u : ref Text;
  636. if(alphabet != ALPHA_LATIN)
  637. r = transc(r, alphabet);
  638. if (echomode == EM_RAW && t.what == Body) {
  639. if (t.w != nil) {
  640. s := "a";
  641. s[0] = r;
  642. t.w.event(sprint("R0 0 0 1 %s\n", s));
  643. }
  644. return;
  645. }
  646. if(t.what!=Body && r=='\n')
  647. return;
  648. case r {
  649. Dat->Kscrolldown=>
  650. if(t.what == Body){
  651. q0 = t.org+frcharofpt(t.frame, (t.frame.r.min.x, t.frame.r.min.y+2*t.frame.font.height));
  652. t.setorigin(q0, FALSE);
  653. }
  654. return;
  655. Dat->Kscrollup=>
  656. if(t.what == Body){
  657. q0 = t.backnl(t.org, 4);
  658. t.setorigin(q0, FALSE);
  659. }
  660. return;
  661. Kdown or Keyboard->Down =>
  662. n = t.frame.maxlines/3;
  663. q0 = t.org+frcharofpt(t.frame, (t.frame.r.min.x, t.frame.r.min.y+n*t.frame.font.height));
  664. t.setorigin(q0, FALSE);
  665. return;
  666. Keyboard->Pgdown =>
  667. n = 2*t.frame.maxlines/3;
  668. q0 = t.org+frcharofpt(t.frame, (t.frame.r.min.x, t.frame.r.min.y+n*t.frame.font.height));
  669. t.setorigin(q0, FALSE);
  670. return;
  671. Kup or Keyboard->Up =>
  672. n = t.frame.maxlines/3;
  673. q0 = t.backnl(t.org, n);
  674. t.setorigin(q0, FALSE);
  675. return;
  676. Keyboard->Pgup =>
  677. n = 2*t.frame.maxlines/3;
  678. q0 = t.backnl(t.org, n);
  679. t.setorigin(q0, FALSE);
  680. return;
  681. Keyboard->Home =>
  682. t.commit(TRUE);
  683. t.show(0, 0, FALSE);
  684. return;
  685. Kend or Keyboard->End =>
  686. t.commit(TRUE);
  687. t.show(t.file.buf.nc, t.file.buf.nc, FALSE);
  688. return;
  689. Kleft or Keyboard->Left =>
  690. t.commit(TRUE);
  691. if(t.q0 != t.q1)
  692. t.show(t.q0, t.q0, TRUE);
  693. else if(t.q0 != 0)
  694. t.show(t.q0-1, t.q0-1, TRUE);
  695. return;
  696. Kright or Keyboard->Right =>
  697. t.commit(TRUE);
  698. if(t.q0 != t.q1)
  699. t.show(t.q1, t.q1, TRUE);
  700. else if(t.q1 != t.file.buf.nc)
  701. t.show(t.q1+1, t.q1+1, TRUE);
  702. return;
  703. 1 => # ^A: beginning of line
  704. t.commit(TRUE);
  705. # go to where ^U would erase, if not already at BOL
  706. nnb = 0;
  707. if(t.q0>0 && t.readc(t.q0-1)!='\n')
  708. nnb = t.bswidth(16r15);
  709. t.show(t.q0-nnb, t.q0-nnb, TRUE);
  710. return;
  711. 5 => # ^E: end of line
  712. t.commit(TRUE);
  713. q0 = t.q0;
  714. while(q0<t.file.buf.nc && t.readc(q0)!='\n')
  715. q0++;
  716. t.show(q0, q0, TRUE);
  717. return;
  718. }
  719. if(t.what == Body){
  720. seq++;
  721. t.file.mark();
  722. }
  723. if(t.q1 > t.q0){
  724. if(t.ncache != 0)
  725. error("text.type");
  726. exec->cut(t, t, TRUE, TRUE);
  727. t.eq0 = ~0;
  728. if (r == 16r08 || r == 16r7f){ # erase character : odd if a char then erased
  729. t.show(t.q0, t.q0,TRUE);
  730. return;
  731. }
  732. }
  733. t.show(t.q0, t.q0, TRUE);
  734. case(r){
  735. 16r1B =>
  736. if(t.eq0 != ~0)
  737. t.setselect(t.eq0, t.q0);
  738. if(t.ncache > 0){
  739. if(t.w != nil)
  740. t.w.commit(t);
  741. else
  742. t.commit(TRUE);
  743. }
  744. return;
  745. 16r08 or 16r15 or 16r17 =>
  746. # ^H: erase character or ^U: erase line or ^W: erase word
  747. if(t.q0 == 0)
  748. return;
  749. if(0) # DEBUGGING
  750. for(i=0; i<t.file.ntext; i++){
  751. u = t.file.text[i];
  752. if(u.cq0!=t.cq0 && (u.ncache!=t.ncache || t.ncache!=0))
  753. error("text.type inconsistent caches");
  754. }
  755. nnb = t.bswidth(r);
  756. q1 = t.q0;
  757. q0 = q1-nnb;
  758. for(i=0; i<t.file.ntext; i++){
  759. u = t.file.text[i];
  760. u.nofill = TRUE;
  761. nb = nnb;
  762. n = u.ncache;
  763. if(n > 0){
  764. if(q1 != u.cq0+n)
  765. error("text.type backspace");
  766. if(n > nb)
  767. n = nb;
  768. u.ncache -= n;
  769. u.delete(q1-n, q1, FALSE);
  770. nb -= n;
  771. }
  772. if(u.eq0==q1 || u.eq0==~0)
  773. u.eq0 = q0;
  774. if(nb && u==t)
  775. u.delete(q0, q0+nb, TRUE);
  776. if(u != t)
  777. u.setselect(u.q0, u.q1);
  778. else
  779. t.setselect(q0, q0);
  780. u.nofill = FALSE;
  781. }
  782. for(i=0; i<t.file.ntext; i++)
  783. t.file.text[i].fill();
  784. return;
  785. 16r7f or Keyboard->Del =>
  786. # Delete character - forward delete
  787. t.commit(TRUE);
  788. if(t.q0 >= t.file.buf.nc)
  789. return;
  790. nnb = 1;
  791. q0 = t.q0;
  792. q1 = q0+nnb;
  793. for(i=0; i<t.file.ntext; i++){
  794. u = t.file.text[i];
  795. if (u!=t)
  796. u.commit(FALSE);
  797. u.nofill = TRUE;
  798. if(u.eq0==q1 || u.eq0==~0)
  799. u.eq0 = q0;
  800. if(u==t)
  801. u.delete(q0, q1, TRUE);
  802. if(u != t)
  803. u.setselect(u.q0, u.q1);
  804. else
  805. t.setselect(q0, q0);
  806. u.nofill = FALSE;
  807. }
  808. for(i=0; i<t.file.ntext; i++)
  809. t.file.text[i].fill();
  810. return;
  811. }
  812. # otherwise ordinary character; just insert, typically in caches of all texts
  813. if(0) # DEBUGGING
  814. for(i=0; i<t.file.ntext; i++){
  815. u = t.file.text[i];
  816. if(u.cq0!=t.cq0 && (u.ncache!=t.ncache || t.ncache!=0))
  817. error("text.type inconsistent caches");
  818. }
  819. for(i=0; i<t.file.ntext; i++){
  820. u = t.file.text[i];
  821. if(u.eq0 == ~0)
  822. u.eq0 = t.q0;
  823. if(u.ncache == 0)
  824. u.cq0 = t.q0;
  825. else if(t.q0 != u.cq0+u.ncache)
  826. error("text.type cq1");
  827. str := "Z";
  828. str[0] = r;
  829. u.insert(t.q0, str, 1, FALSE, echomode);
  830. str = nil;
  831. if(u != t)
  832. u.setselect(u.q0, u.q1);
  833. if(u.ncache == u.ncachealloc){
  834. u.ncachealloc += 10;
  835. u.cache += "1234567890";
  836. }
  837. u.cache[u.ncache++] = r;
  838. }
  839. t.setselect(t.q0+1, t.q0+1);
  840. if(r=='\n' && t.w!=nil)
  841. t.w.commit(t);
  842. }
  843. Text.commit(t : self ref Text, tofile : int)
  844. {
  845. if(t.ncache == 0)
  846. return;
  847. if(tofile)
  848. t.file.insert(t.cq0, t.cache, t.ncache);
  849. if(t.what == Body){
  850. t.w.dirty = TRUE;
  851. t.w.utflastqid = -1;
  852. }
  853. t.ncache = 0;
  854. }
  855. clicktext : ref Text;
  856. clickmsec : int = 0;
  857. selecttext : ref Text;
  858. selectq : int = 0;
  859. #
  860. # called from frame library
  861. #
  862. framescroll(f : ref Frame, dl : int)
  863. {
  864. if(f != selecttext.frame)
  865. error("frameselect not right frame");
  866. selecttext.framescroll(dl);
  867. }
  868. Text.framescroll(t : self ref Text, dl : int)
  869. {
  870. q0 : int;
  871. if(dl == 0){
  872. scrl->scrsleep(100);
  873. return;
  874. }
  875. if(dl < 0){
  876. q0 = t.backnl(t.org, -dl);
  877. if(selectq > t.org+t.frame.p0)
  878. t.setselect0(t.org+t.frame.p0, selectq);
  879. else
  880. t.setselect0(selectq, t.org+t.frame.p0);
  881. }else{
  882. if(t.org+t.frame.nchars == t.file.buf.nc)
  883. return;
  884. q0 = t.org+frcharofpt(t.frame, (t.frame.r.min.x, t.frame.r.min.y+dl*t.frame.font.height));
  885. if(selectq > t.org+t.frame.p1)
  886. t.setselect0(t.org+t.frame.p1, selectq);
  887. else
  888. t.setselect0(selectq, t.org+t.frame.p1);
  889. }
  890. t.setorigin(q0, TRUE);
  891. }
  892. Text.select(t : self ref Text, double : int)
  893. {
  894. q0, q1 : int;
  895. b, x, y : int;
  896. state : int;
  897. selecttext = t;
  898. #
  899. # To have double-clicking and chording, we double-click
  900. # immediately if it might make sense.
  901. #
  902. b = mouse.buttons;
  903. q0 = t.q0;
  904. q1 = t.q1;
  905. selectq = t.org+frcharofpt(t.frame, mouse.xy);
  906. if(double || (clicktext==t && mouse.msec-clickmsec<500))
  907. if(q0==q1 && selectq==q0){
  908. (q0, q1) = t.doubleclick(q0, q1);
  909. t.setselect(q0, q1);
  910. bflush();
  911. x = mouse.xy.x;
  912. y = mouse.xy.y;
  913. # stay here until something interesting happens
  914. do
  915. frgetmouse();
  916. while(mouse.buttons==b && utils->abs(mouse.xy.x-x)<3 && utils->abs(mouse.xy.y-y)<3);
  917. mouse.xy.x = x; # in case we're calling frselect
  918. mouse.xy.y = y;
  919. q0 = t.q0; # may have changed
  920. q1 = t.q1;
  921. selectq = q0;
  922. }
  923. if(mouse.buttons == b){
  924. t.frame.scroll = 1;
  925. frselect(t.frame, mouse);
  926. # horrible botch: while asleep, may have lost selection altogether
  927. if(selectq > t.file.buf.nc)
  928. selectq = t.org + t.frame.p0;
  929. t.frame.scroll = 0;
  930. if(selectq < t.org)
  931. q0 = selectq;
  932. else
  933. q0 = t.org + t.frame.p0;
  934. if(selectq > t.org+t.frame.nchars)
  935. q1 = selectq;
  936. else
  937. q1 = t.org+t.frame.p1;
  938. }
  939. if(q0 == q1){
  940. if(q0==t.q0 && (double || clicktext==t && mouse.msec-clickmsec<500)){
  941. (q0, q1) = t.doubleclick(q0, q1);
  942. clicktext = nil;
  943. }else{
  944. clicktext = t;
  945. clickmsec = mouse.msec;
  946. }
  947. }else
  948. clicktext = nil;
  949. t.setselect(q0, q1);
  950. bflush();
  951. state = 0; # undo when possible; +1 for cut, -1 for paste
  952. while(mouse.buttons){
  953. mouse.msec = 0;
  954. b = mouse.buttons;
  955. if(b & 6){
  956. if(state==0 && t.what==Body){
  957. seq++;
  958. t.w.body.file.mark();
  959. }
  960. if(b & 2){
  961. if(state==-1 && t.what==Body){
  962. t.w.undo(TRUE);
  963. t.setselect(q0, t.q0);
  964. state = 0;
  965. }else if(state != 1){
  966. exec->cut(t, t, TRUE, TRUE);
  967. state = 1;
  968. }
  969. }else{
  970. if(state==1 && t.what==Body){
  971. t.w.undo(TRUE);
  972. t.setselect(q0, t.q1);
  973. state = 0;
  974. }else if(state != -1){
  975. exec->paste(t, t, TRUE, FALSE);
  976. state = -1;
  977. }
  978. }
  979. scrdraw(t);
  980. utils->clearmouse();
  981. }
  982. bflush();
  983. while(mouse.buttons == b)
  984. frgetmouse();
  985. clicktext = nil;
  986. }
  987. }
  988. Text.show(t : self ref Text, q0 : int, q1 : int, doselect : int)
  989. {
  990. qe : int;
  991. nl : int;
  992. q : int;
  993. if(t.what != Body){
  994. if(doselect)
  995. t.setselect(q0, q1);
  996. return;
  997. }
  998. if(t.w!=nil && t.frame.maxlines==0)
  999. t.col.grow(t.w, 1, 0);
  1000. if(doselect)
  1001. t.setselect(q0, q1);
  1002. qe = t.org+t.frame.nchars;
  1003. if(t.org<=q0 && (q0<qe || (q0==qe && qe==t.file.buf.nc+t.ncache)))
  1004. scrdraw(t);
  1005. else{
  1006. if(t.w.nopen[Dat->QWevent]>byte 0)
  1007. nl = 3*t.frame.maxlines/4;
  1008. else
  1009. nl = t.frame.maxlines/4;
  1010. q = t.backnl(q0, nl);
  1011. # avoid going backwards if trying to go forwards - long lines!
  1012. if(!(q0>t.org && q<t.org))
  1013. t.setorigin(q, TRUE);
  1014. while(q0 > t.org+t.frame.nchars)
  1015. t.setorigin(t.org+1, FALSE);
  1016. }
  1017. }
  1018. region(a, b : int) : int
  1019. {
  1020. if(a < b)
  1021. return -1;
  1022. if(a == b)
  1023. return 0;
  1024. return 1;
  1025. }
  1026. selrestore(f : ref Frame, pt0 : Point, p0 : int, p1 : int)
  1027. {
  1028. if(p1<=f.p0 || p0>=f.p1){
  1029. # no overlap
  1030. frdrawsel0(f, pt0, p0, p1, f.cols[BACK], f.cols[TEXT]);
  1031. return;
  1032. }
  1033. if(p0>=f.p0 && p1<=f.p1){
  1034. # entirely inside
  1035. frdrawsel0(f, pt0, p0, p1, f.cols[HIGH], f.cols[HTEXT]);
  1036. return;
  1037. }
  1038. # they now are known to overlap
  1039. # before selection
  1040. if(p0 < f.p0){
  1041. frdrawsel0(f, pt0, p0, f.p0, f.cols[BACK], f.cols[TEXT]);
  1042. p0 = f.p0;
  1043. pt0 = frptofchar(f, p0);
  1044. }
  1045. # after selection
  1046. if(p1 > f.p1){
  1047. frdrawsel0(f, frptofchar(f, f.p1), f.p1, p1, f.cols[BACK], f.cols[TEXT]);
  1048. p1 = f.p1;
  1049. }
  1050. # inside selection
  1051. frdrawsel0(f, pt0, p0, p1, f.cols[HIGH], f.cols[HTEXT]);
  1052. }
  1053. Text.setselect(t : self ref Text, q0 : int, q1 : int)
  1054. {
  1055. p0, p1 : int;
  1056. # t.p0 and t.p1 are always right; t.q0 and t.q1 may be off
  1057. t.q0 = q0;
  1058. t.q1 = q1;
  1059. # compute desired p0,p1 from q0,q1
  1060. p0 = q0-t.org;
  1061. p1 = q1-t.org;
  1062. if(p0 < 0)
  1063. p0 = 0;
  1064. if(p1 < 0)
  1065. p1 = 0;
  1066. if(p0 > t.frame.nchars)
  1067. p0 = t.frame.nchars;
  1068. if(p1 > t.frame.nchars)
  1069. p1 = t.frame.nchars;
  1070. if(p0==t.frame.p0 && p1==t.frame.p1)
  1071. return;
  1072. # screen disagrees with desired selection
  1073. if(t.frame.p1<=p0 || p1<=t.frame.p0 || p0==p1 || t.frame.p1==t.frame.p0){
  1074. # no overlap or too easy to bother trying
  1075. frdrawsel(t.frame, frptofchar(t.frame, t.frame.p0), t.frame.p0, t.frame.p1, 0);
  1076. frdrawsel(t.frame, frptofchar(t.frame, p0), p0, p1, 1);
  1077. t.frame.p0 = p0;
  1078. t.frame.p1 = p1;
  1079. return;
  1080. }
  1081. # overlap; avoid unnecessary painting
  1082. if(p0 < t.frame.p0){
  1083. # extend selection backwards
  1084. frdrawsel(t.frame, frptofchar(t.frame, p0), p0, t.frame.p0, 1);
  1085. }else if(p0 > t.frame.p0){
  1086. # trim first part of selection
  1087. frdrawsel(t.frame, frptofchar(t.frame, t.frame.p0), t.frame.p0, p0, 0);
  1088. }
  1089. if(p1 > t.frame.p1){
  1090. # extend selection forwards
  1091. frdrawsel(t.frame, frptofchar(t.frame, t.frame.p1), t.frame.p1, p1, 1);
  1092. }else if(p1 < t.frame.p1){
  1093. # trim last part of selection
  1094. frdrawsel(t.frame, frptofchar(t.frame, p1), p1, t.frame.p1, 0);
  1095. }
  1096. t.frame.p0 = p0;
  1097. t.frame.p1 = p1;
  1098. }
  1099. Text.setselect0(t : self ref Text, q0 : int, q1 : int)
  1100. {
  1101. t.q0 = q0;
  1102. t.q1 = q1;
  1103. }
  1104. xselect(f : ref Frame, mc : ref Draw->Pointer, col, colt : ref Image) : (int, int)
  1105. {
  1106. p0, p1, q, tmp : int;
  1107. mp, pt0, pt1, qt : Point;
  1108. reg, b : int;
  1109. # when called button 1 is down
  1110. mp = mc.xy;
  1111. b = mc.buttons;
  1112. # remove tick
  1113. if(f.p0 == f.p1)
  1114. frtick(f, frptofchar(f, f.p0), 0);
  1115. p0 = p1 = frcharofpt(f, mp);
  1116. pt0 = frptofchar(f, p0);
  1117. pt1 = frptofchar(f, p1);
  1118. reg = 0;
  1119. frtick(f, pt0, 1);
  1120. do{
  1121. q = frcharofpt(f, mc.xy);
  1122. if(p1 != q){
  1123. if(p0 == p1)
  1124. frtick(f, pt0, 0);
  1125. if(reg != region(q, p0)){ # crossed starting point; reset
  1126. if(reg > 0)
  1127. selrestore(f, pt0, p0, p1);
  1128. else if(reg < 0)
  1129. selrestore(f, pt1, p1, p0);
  1130. p1 = p0;
  1131. pt1 = pt0;
  1132. reg = region(q, p0);
  1133. if(reg == 0)
  1134. frdrawsel0(f, pt0, p0, p1, col, colt);
  1135. }
  1136. qt = frptofchar(f, q);
  1137. if(reg > 0){
  1138. if(q > p1)
  1139. frdrawsel0(f, pt1, p1, q, col, colt);
  1140. else if(q < p1)
  1141. selrestore(f, qt, q, p1);
  1142. }else if(reg < 0){
  1143. if(q > p1)
  1144. selrestore(f, pt1, p1, q);
  1145. else
  1146. frdrawsel0(f, qt, q, p1, col, colt);
  1147. }
  1148. p1 = q;
  1149. pt1 = qt;
  1150. }
  1151. if(p0 == p1)
  1152. frtick(f, pt0, 1);
  1153. bflush();
  1154. frgetmouse();
  1155. }while(mc.buttons == b);
  1156. if(p1 < p0){
  1157. tmp = p0;
  1158. p0 = p1;
  1159. p1 = tmp;
  1160. }
  1161. pt0 = frptofchar(f, p0);
  1162. if(p0 == p1)
  1163. frtick(f, pt0, 0);
  1164. selrestore(f, pt0, p0, p1);
  1165. # restore tick
  1166. if(f.p0 == f.p1)
  1167. frtick(f, frptofchar(f, f.p0), 1);
  1168. bflush();
  1169. return (p0, p1);
  1170. }
  1171. Text.select23(t : self ref Text, q0 : int, q1 : int, high, low : ref Image, mask : int) : (int, int, int)
  1172. {
  1173. p0, p1 : int;
  1174. buts : int;
  1175. (p0, p1) = xselect(t.frame, mouse, high, low);
  1176. buts = mouse.buttons;
  1177. if((buts & mask) == 0){
  1178. q0 = p0+t.org;
  1179. q1 = p1+t.org;
  1180. }
  1181. while(mouse.buttons)
  1182. frgetmouse();
  1183. return (buts, q0, q1);
  1184. }
  1185. Text.select2(t : self ref Text, q0 : int, q1 : int) : (int, ref Text, int, int)
  1186. {
  1187. buts : int;
  1188. (buts, q0, q1) = t.select23(q0, q1, acme->but2col, acme->but2colt, 4);
  1189. if(buts & 4)
  1190. return (0, nil, q0, q1);
  1191. if(buts & 1) # pick up argument
  1192. return (1, dat->argtext, q0, q1);
  1193. return (1, nil, q0, q1);
  1194. }
  1195. Text.select3(t : self ref Text, q0 : int, q1 : int) : (int, int, int)
  1196. {
  1197. buts : int;
  1198. (buts, q0, q1) = t.select23(q0, q1, acme->but3col, acme->but3colt, 1|2);
  1199. return (buts == 0, q0, q1);
  1200. }
  1201. left := array[4] of {
  1202. "{[(<«",
  1203. "\n",
  1204. "'\"`",
  1205. nil
  1206. };
  1207. right := array[4] of {
  1208. "}])>»",
  1209. "\n",
  1210. "'\"`",
  1211. nil
  1212. };
  1213. Text.doubleclick(t : self ref Text, q0 : int, q1 : int) : (int, int)
  1214. {
  1215. c, i : int;
  1216. r, l : string;
  1217. p : int;
  1218. q : int;
  1219. res : int;
  1220. for(i=0; left[i]!=nil; i++){
  1221. q = q0;
  1222. l = left[i];
  1223. r = right[i];
  1224. # try matching character to left, looking right
  1225. if(q == 0)
  1226. c = '\n';
  1227. else
  1228. c = t.readc(q-1);
  1229. p = utils->strchr(l, c);
  1230. if(p >= 0){
  1231. (res, q) = t.clickmatch(c, r[p], 1, q);
  1232. if (res)
  1233. q1 = q-(c!='\n');
  1234. return (q0, q1);
  1235. }
  1236. # try matching character to right, looking left
  1237. if(q == t.file.buf.nc)
  1238. c = '\n';
  1239. else
  1240. c = t.readc(q);
  1241. p = utils->strchr(r, c);
  1242. if(p >= 0){
  1243. (res, q) = t.clickmatch(c, l[p], -1, q);
  1244. if (res){
  1245. q1 = q0+(q0<t.file.buf.nc && c=='\n');
  1246. q0 = q;
  1247. if(c!='\n' || q!=0 || t.readc(0)=='\n')
  1248. q0++;
  1249. }
  1250. return (q0, q1);
  1251. }
  1252. }
  1253. # try filling out word to right
  1254. while(q1<t.file.buf.nc && isalnum(t.readc(q1)))
  1255. q1++;
  1256. # try filling out word to left
  1257. while(q0>0 && isalnum(t.readc(q0-1)))
  1258. q0--;
  1259. return (q0, q1);
  1260. }
  1261. Text.clickmatch(t : self ref Text, cl : int, cr : int, dir : int, q : int) : (int, int)
  1262. {
  1263. c : int;
  1264. nest : int;
  1265. nest = 1;
  1266. for(;;){
  1267. if(dir > 0){
  1268. if(q == t.file.buf.nc)
  1269. break;
  1270. c = t.readc(q);
  1271. q++;
  1272. }else{
  1273. if(q == 0)
  1274. break;
  1275. q--;
  1276. c = t.readc(q);
  1277. }
  1278. if(c == cr){
  1279. if(--nest==0)
  1280. return (1, q);
  1281. }else if(c == cl)
  1282. nest++;
  1283. }
  1284. return (cl=='\n' && nest==1, q);
  1285. }
  1286. Text.forwnl(t : self ref Text, p : int, n : int) : int
  1287. {
  1288. i, j : int;
  1289. e := t.file.buf.nc-1;
  1290. i = n;
  1291. while(i-- > 0 && p<e){
  1292. ++p;
  1293. if(p == e)
  1294. break;
  1295. for(j=128; --j>0 && p<e; p++)
  1296. if(t.readc(p)=='\n')
  1297. break;
  1298. }
  1299. return p;
  1300. }
  1301. Text.backnl(t : self ref Text, p : int, n : int) : int
  1302. {
  1303. i, j : int;
  1304. # look for start of this line if n==0
  1305. if(n==0 && p>0 && t.readc(p-1)!='\n')
  1306. n = 1;
  1307. i = n;
  1308. while(i-- > 0 && p>0){
  1309. --p; # it's at a newline now; back over it
  1310. if(p == 0)
  1311. break;
  1312. # at 128 chars, call it a line anyway
  1313. for(j=128; --j>0 && p>0; p--)
  1314. if(t.readc(p-1)=='\n')
  1315. break;
  1316. }
  1317. return p;
  1318. }
  1319. Text.setorigin(t : self ref Text, org : int, exact : int)
  1320. {
  1321. i, a : int;
  1322. r : ref Astring;
  1323. n : int;
  1324. t.frame.b.flush(Flushoff);
  1325. if(org>0 && !exact){
  1326. # org is an estimate of the char posn; find a newline
  1327. # don't try harder than 256 chars
  1328. for(i=0; i<256 && org<t.file.buf.nc; i++){
  1329. if(t.readc(org) == '\n'){
  1330. org++;
  1331. break;
  1332. }
  1333. org++;
  1334. }
  1335. }
  1336. a = org-t.org;
  1337. fixup := 0;
  1338. if(a>=0 && a<t.frame.nchars){
  1339. frdelete(t.frame, 0, a);
  1340. fixup = 1; # frdelete can leave end of last line in wrong selection mode; it doesn't know what follows
  1341. }
  1342. else if(a<0 && -a<t.frame.nchars){
  1343. n = t.org - org;
  1344. r = utils->stralloc(n);
  1345. t.file.buf.read(org, r, 0, n);
  1346. frinsert(t.frame, r.s, n, 0);
  1347. utils->strfree(r);
  1348. r = nil;
  1349. }else
  1350. frdelete(t.frame, 0, t.frame.nchars);
  1351. t.org = org;
  1352. t.fill();
  1353. scrdraw(t);
  1354. t.setselect(t.q0, t.q1);
  1355. if(fixup && t.frame.p1 > t.frame.p0)
  1356. frdrawsel(t.frame, frptofchar(t.frame, t.frame.p1-1), t.frame.p1-1, t.frame.p1, 1);
  1357. t.frame.b.flush(Flushon);
  1358. }
  1359. Text.reset(t : self ref Text)
  1360. {
  1361. t.file.seq = 0;
  1362. t.eq0 = ~0;
  1363. # do t.delete(0, t.nc, TRUE) without building backup stuff
  1364. t.setselect(t.org, t.org);
  1365. frdelete(t.frame, 0, t.frame.nchars);
  1366. t.org = 0;
  1367. t.q0 = 0;
  1368. t.q1 = 0;
  1369. t.file.reset();
  1370. t.file.buf.reset();
  1371. }