text.c 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <draw.h>
  4. #include <thread.h>
  5. #include <cursor.h>
  6. #include <mouse.h>
  7. #include <keyboard.h>
  8. #include <frame.h>
  9. #include <fcall.h>
  10. #include <plumb.h>
  11. #include <complete.h>
  12. #include "dat.h"
  13. #include "fns.h"
  14. Image *tagcols[NCOL];
  15. Image *textcols[NCOL];
  16. enum{
  17. TABDIR = 3 /* width of tabs in directory windows */
  18. };
  19. void
  20. textinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL])
  21. {
  22. t->file = f;
  23. t->all = r;
  24. t->scrollr = r;
  25. t->scrollr.max.x = r.min.x+Scrollwid;
  26. t->lastsr = nullrect;
  27. r.min.x += Scrollwid+Scrollgap;
  28. t->eq0 = ~0;
  29. t->ncache = 0;
  30. t->reffont = rf;
  31. t->tabstop = maxtab;
  32. memmove(t->Frame.cols, cols, sizeof t->Frame.cols);
  33. textredraw(t, r, rf->f, screen, -1);
  34. }
  35. void
  36. textredraw(Text *t, Rectangle r, Font *f, Image *b, int odx)
  37. {
  38. int maxt;
  39. Rectangle rr;
  40. frinit(t, r, f, b, t->Frame.cols);
  41. rr = t->r;
  42. rr.min.x -= Scrollwid; /* back fill to scroll bar */
  43. draw(t->b, rr, t->cols[BACK], nil, ZP);
  44. /* use no wider than 3-space tabs in a directory */
  45. maxt = maxtab;
  46. if(t->what == Body){
  47. if(t->w->isdir)
  48. maxt = min(TABDIR, maxtab);
  49. else
  50. maxt = t->tabstop;
  51. }
  52. t->maxtab = maxt*stringwidth(f, "0");
  53. if(t->what==Body && t->w->isdir && odx!=Dx(t->all)){
  54. if(t->maxlines > 0){
  55. textreset(t);
  56. textcolumnate(t, t->w->dlp, t->w->ndl);
  57. textshow(t, 0, 0, 1);
  58. }
  59. }else{
  60. textfill(t);
  61. textsetselect(t, t->q0, t->q1);
  62. }
  63. }
  64. int
  65. textresize(Text *t, Rectangle r)
  66. {
  67. int odx;
  68. if(Dy(r) > 0)
  69. r.max.y -= Dy(r)%t->font->height;
  70. else
  71. r.max.y = r.min.y;
  72. odx = Dx(t->all);
  73. t->all = r;
  74. t->scrollr = r;
  75. t->scrollr.max.x = r.min.x+Scrollwid;
  76. t->lastsr = nullrect;
  77. r.min.x += Scrollwid+Scrollgap;
  78. frclear(t, 0);
  79. textredraw(t, r, t->font, t->b, odx);
  80. return r.max.y;
  81. }
  82. void
  83. textclose(Text *t)
  84. {
  85. free(t->cache);
  86. frclear(t, 1);
  87. filedeltext(t->file, t);
  88. t->file = nil;
  89. rfclose(t->reffont);
  90. if(argtext == t)
  91. argtext = nil;
  92. if(typetext == t)
  93. typetext = nil;
  94. if(seltext == t)
  95. seltext = nil;
  96. if(mousetext == t)
  97. mousetext = nil;
  98. if(barttext == t)
  99. barttext = nil;
  100. }
  101. int
  102. dircmp(void *a, void *b)
  103. {
  104. Dirlist *da, *db;
  105. int i, n;
  106. da = *(Dirlist**)a;
  107. db = *(Dirlist**)b;
  108. n = min(da->nr, db->nr);
  109. i = memcmp(da->r, db->r, n*sizeof(Rune));
  110. if(i)
  111. return i;
  112. return da->nr - db->nr;
  113. }
  114. void
  115. textcolumnate(Text *t, Dirlist **dlp, int ndl)
  116. {
  117. int i, j, w, colw, mint, maxt, ncol, nrow;
  118. Dirlist *dl;
  119. uint q1;
  120. if(t->file->ntext > 1)
  121. return;
  122. mint = stringwidth(t->font, "0");
  123. /* go for narrower tabs if set more than 3 wide */
  124. t->maxtab = min(maxtab, TABDIR)*mint;
  125. maxt = t->maxtab;
  126. colw = 0;
  127. for(i=0; i<ndl; i++){
  128. dl = dlp[i];
  129. w = dl->wid;
  130. if(maxt-w%maxt < mint || w%maxt==0)
  131. w += mint;
  132. if(w % maxt)
  133. w += maxt-(w%maxt);
  134. if(w > colw)
  135. colw = w;
  136. }
  137. if(colw == 0)
  138. ncol = 1;
  139. else
  140. ncol = max(1, Dx(t->r)/colw);
  141. nrow = (ndl+ncol-1)/ncol;
  142. q1 = 0;
  143. for(i=0; i<nrow; i++){
  144. for(j=i; j<ndl; j+=nrow){
  145. dl = dlp[j];
  146. fileinsert(t->file, q1, dl->r, dl->nr);
  147. q1 += dl->nr;
  148. if(j+nrow >= ndl)
  149. break;
  150. w = dl->wid;
  151. if(maxt-w%maxt < mint){
  152. fileinsert(t->file, q1, L"\t", 1);
  153. q1++;
  154. w += mint;
  155. }
  156. do{
  157. fileinsert(t->file, q1, L"\t", 1);
  158. q1++;
  159. w += maxt-(w%maxt);
  160. }while(w < colw);
  161. }
  162. fileinsert(t->file, q1, L"\n", 1);
  163. q1++;
  164. }
  165. }
  166. uint
  167. textload(Text *t, uint q0, char *file, int setqid)
  168. {
  169. Rune *rp;
  170. Dirlist *dl, **dlp;
  171. int fd, i, j, n, ndl, nulls;
  172. uint q, q1;
  173. Dir *d, *dbuf;
  174. char *tmp;
  175. Text *u;
  176. if(t->ncache!=0 || t->file->nc || t->w==nil || t!=&t->w->body)
  177. error("text.load");
  178. if(t->w->isdir && t->file->nname==0){
  179. warning(nil, "empty directory name\n");
  180. return 0;
  181. }
  182. fd = open(file, OREAD);
  183. if(fd < 0){
  184. warning(nil, "can't open %s: %r\n", file);
  185. return 0;
  186. }
  187. d = dirfstat(fd);
  188. if(d == nil){
  189. warning(nil, "can't fstat %s: %r\n", file);
  190. goto Rescue;
  191. }
  192. nulls = FALSE;
  193. if(d->qid.type & QTDIR){
  194. /* this is checked in get() but it's possible the file changed underfoot */
  195. if(t->file->ntext > 1){
  196. warning(nil, "%s is a directory; can't read with multiple windows on it\n", file);
  197. goto Rescue;
  198. }
  199. t->w->isdir = TRUE;
  200. t->w->filemenu = FALSE;
  201. if(t->file->name[t->file->nname-1] != '/'){
  202. rp = runemalloc(t->file->nname+1);
  203. runemove(rp, t->file->name, t->file->nname);
  204. rp[t->file->nname] = '/';
  205. winsetname(t->w, rp, t->file->nname+1);
  206. free(rp);
  207. }
  208. dlp = nil;
  209. ndl = 0;
  210. dbuf = nil;
  211. while((n=dirread(fd, &dbuf)) > 0){
  212. for(i=0; i<n; i++){
  213. dl = emalloc(sizeof(Dirlist));
  214. j = strlen(dbuf[i].name);
  215. tmp = emalloc(j+1+1);
  216. memmove(tmp, dbuf[i].name, j);
  217. if(dbuf[i].qid.type & QTDIR)
  218. tmp[j++] = '/';
  219. tmp[j] = '\0';
  220. dl->r = bytetorune(tmp, &dl->nr);
  221. dl->wid = stringwidth(t->font, tmp);
  222. free(tmp);
  223. ndl++;
  224. dlp = realloc(dlp, ndl*sizeof(Dirlist*));
  225. dlp[ndl-1] = dl;
  226. }
  227. free(dbuf);
  228. }
  229. qsort(dlp, ndl, sizeof(Dirlist*), dircmp);
  230. t->w->dlp = dlp;
  231. t->w->ndl = ndl;
  232. textcolumnate(t, dlp, ndl);
  233. q1 = t->file->nc;
  234. }else{
  235. t->w->isdir = FALSE;
  236. t->w->filemenu = TRUE;
  237. q1 = q0 + fileload(t->file, q0, fd, &nulls);
  238. }
  239. if(setqid){
  240. t->file->dev = d->dev;
  241. t->file->mtime = d->mtime;
  242. t->file->qidpath = d->qid.path;
  243. }
  244. close(fd);
  245. rp = fbufalloc();
  246. for(q=q0; q<q1; q+=n){
  247. n = q1-q;
  248. if(n > RBUFSIZE)
  249. n = RBUFSIZE;
  250. bufread(t->file, q, rp, n);
  251. if(q < t->org)
  252. t->org += n;
  253. else if(q <= t->org+t->nchars)
  254. frinsert(t, rp, rp+n, q-t->org);
  255. if(t->lastlinefull)
  256. break;
  257. }
  258. fbuffree(rp);
  259. for(i=0; i<t->file->ntext; i++){
  260. u = t->file->text[i];
  261. if(u != t){
  262. if(u->org > u->file->nc) /* will be 0 because of reset(), but safety first */
  263. u->org = 0;
  264. textresize(u, u->all);
  265. textbacknl(u, u->org, 0); /* go to beginning of line */
  266. }
  267. textsetselect(u, q0, q0);
  268. }
  269. if(nulls)
  270. warning(nil, "%s: NUL bytes elided\n", file);
  271. free(d);
  272. return q1-q0;
  273. Rescue:
  274. close(fd);
  275. return 0;
  276. }
  277. uint
  278. textbsinsert(Text *t, uint q0, Rune *r, uint n, int tofile, int *nrp)
  279. {
  280. Rune *bp, *tp, *up;
  281. int i, initial;
  282. if(t->what == Tag){ /* can't happen but safety first: mustn't backspace over file name */
  283. Err:
  284. textinsert(t, q0, r, n, tofile);
  285. *nrp = n;
  286. return q0;
  287. }
  288. bp = r;
  289. for(i=0; i<n; i++)
  290. if(*bp++ == '\b'){
  291. --bp;
  292. initial = 0;
  293. tp = runemalloc(n);
  294. runemove(tp, r, i);
  295. up = tp+i;
  296. for(; i<n; i++){
  297. *up = *bp++;
  298. if(*up == '\b')
  299. if(up == tp)
  300. initial++;
  301. else
  302. --up;
  303. else
  304. up++;
  305. }
  306. if(initial){
  307. if(initial > q0)
  308. initial = q0;
  309. q0 -= initial;
  310. textdelete(t, q0, q0+initial, tofile);
  311. }
  312. n = up-tp;
  313. textinsert(t, q0, tp, n, tofile);
  314. free(tp);
  315. *nrp = n;
  316. return q0;
  317. }
  318. goto Err;
  319. }
  320. void
  321. textinsert(Text *t, uint q0, Rune *r, uint n, int tofile)
  322. {
  323. int c, i;
  324. Text *u;
  325. if(tofile && t->ncache != 0)
  326. error("text.insert");
  327. if(n == 0)
  328. return;
  329. if(tofile){
  330. fileinsert(t->file, q0, r, n);
  331. if(t->what == Body){
  332. t->w->dirty = TRUE;
  333. t->w->utflastqid = -1;
  334. }
  335. if(t->file->ntext > 1)
  336. for(i=0; i<t->file->ntext; i++){
  337. u = t->file->text[i];
  338. if(u != t){
  339. u->w->dirty = TRUE; /* always a body */
  340. textinsert(u, q0, r, n, FALSE);
  341. textsetselect(u, u->q0, u->q1);
  342. textscrdraw(u);
  343. }
  344. }
  345. }
  346. if(q0 < t->q1)
  347. t->q1 += n;
  348. if(q0 < t->q0)
  349. t->q0 += n;
  350. if(q0 < t->org)
  351. t->org += n;
  352. else if(q0 <= t->org+t->nchars)
  353. frinsert(t, r, r+n, q0-t->org);
  354. if(t->w){
  355. c = 'i';
  356. if(t->what == Body)
  357. c = 'I';
  358. if(n <= EVENTSIZE)
  359. winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q0+n, n, n, r);
  360. else
  361. winevent(t->w, "%c%d %d 0 0 \n", c, q0, q0+n, n);
  362. }
  363. }
  364. void
  365. textfill(Text *t)
  366. {
  367. Rune *rp;
  368. int i, n, m, nl;
  369. if(t->lastlinefull || t->nofill)
  370. return;
  371. if(t->ncache > 0){
  372. if(t->w != nil)
  373. wincommit(t->w, t);
  374. else
  375. textcommit(t, TRUE);
  376. }
  377. rp = fbufalloc();
  378. do{
  379. n = t->file->nc-(t->org+t->nchars);
  380. if(n == 0)
  381. break;
  382. if(n > 2000) /* educated guess at reasonable amount */
  383. n = 2000;
  384. bufread(t->file, t->org+t->nchars, rp, n);
  385. /*
  386. * it's expensive to frinsert more than we need, so
  387. * count newlines.
  388. */
  389. nl = t->maxlines-t->nlines;
  390. m = 0;
  391. for(i=0; i<n; ){
  392. if(rp[i++] == '\n'){
  393. m++;
  394. if(m >= nl)
  395. break;
  396. }
  397. }
  398. frinsert(t, rp, rp+i, t->nchars);
  399. }while(t->lastlinefull == FALSE);
  400. fbuffree(rp);
  401. }
  402. void
  403. textdelete(Text *t, uint q0, uint q1, int tofile)
  404. {
  405. uint n, p0, p1;
  406. int i, c;
  407. Text *u;
  408. if(tofile && t->ncache != 0)
  409. error("text.delete");
  410. n = q1-q0;
  411. if(n == 0)
  412. return;
  413. if(tofile){
  414. filedelete(t->file, q0, q1);
  415. if(t->what == Body){
  416. t->w->dirty = TRUE;
  417. t->w->utflastqid = -1;
  418. }
  419. if(t->file->ntext > 1)
  420. for(i=0; i<t->file->ntext; i++){
  421. u = t->file->text[i];
  422. if(u != t){
  423. u->w->dirty = TRUE; /* always a body */
  424. textdelete(u, q0, q1, FALSE);
  425. textsetselect(u, u->q0, u->q1);
  426. textscrdraw(u);
  427. }
  428. }
  429. }
  430. if(q0 < t->q0)
  431. t->q0 -= min(n, t->q0-q0);
  432. if(q0 < t->q1)
  433. t->q1 -= min(n, t->q1-q0);
  434. if(q1 <= t->org)
  435. t->org -= n;
  436. else if(q0 < t->org+t->nchars){
  437. p1 = q1 - t->org;
  438. if(p1 > t->nchars)
  439. p1 = t->nchars;
  440. if(q0 < t->org){
  441. t->org = q0;
  442. p0 = 0;
  443. }else
  444. p0 = q0 - t->org;
  445. frdelete(t, p0, p1);
  446. textfill(t);
  447. }
  448. if(t->w){
  449. c = 'd';
  450. if(t->what == Body)
  451. c = 'D';
  452. winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1);
  453. }
  454. }
  455. void
  456. textconstrain(Text *t, uint q0, uint q1, uint *p0, uint *p1)
  457. {
  458. *p0 = min(q0, t->file->nc);
  459. *p1 = min(q1, t->file->nc);
  460. }
  461. Rune
  462. textreadc(Text *t, uint q)
  463. {
  464. Rune r;
  465. if(t->cq0<=q && q<t->cq0+t->ncache)
  466. r = t->cache[q-t->cq0];
  467. else
  468. bufread(t->file, q, &r, 1);
  469. return r;
  470. }
  471. int
  472. textbswidth(Text *t, Rune c)
  473. {
  474. uint q, eq;
  475. Rune r;
  476. int skipping;
  477. /* there is known to be at least one character to erase */
  478. if(c == 0x08) /* ^H: erase character */
  479. return 1;
  480. q = t->q0;
  481. skipping = TRUE;
  482. while(q > 0){
  483. r = textreadc(t, q-1);
  484. if(r == '\n'){ /* eat at most one more character */
  485. if(q == t->q0) /* eat the newline */
  486. --q;
  487. break;
  488. }
  489. if(c == 0x17){
  490. eq = isalnum(r);
  491. if(eq && skipping) /* found one; stop skipping */
  492. skipping = FALSE;
  493. else if(!eq && !skipping)
  494. break;
  495. }
  496. --q;
  497. }
  498. return t->q0-q;
  499. }
  500. int
  501. textfilewidth(Text *t, uint q0, int oneelement)
  502. {
  503. uint q;
  504. Rune r;
  505. q = q0;
  506. while(q > 0){
  507. r = textreadc(t, q-1);
  508. if(r <= ' ')
  509. break;
  510. if(oneelement && r=='/')
  511. break;
  512. --q;
  513. }
  514. return q0-q;
  515. }
  516. Rune*
  517. textcomplete(Text *t)
  518. {
  519. int i, nstr, npath;
  520. uint q;
  521. Rune tmp[200];
  522. Rune *str, *path;
  523. Rune *rp;
  524. Completion *c;
  525. char *s, *dirs;
  526. Runestr dir;
  527. /* control-f: filename completion; works back to white space or / */
  528. if(t->q0<t->file->nc && textreadc(t, t->q0)>' ') /* must be at end of word */
  529. return nil;
  530. nstr = textfilewidth(t, t->q0, TRUE);
  531. str = runemalloc(nstr);
  532. npath = textfilewidth(t, t->q0-nstr, FALSE);
  533. path = runemalloc(npath);
  534. c = nil;
  535. rp = nil;
  536. dirs = nil;
  537. q = t->q0-nstr;
  538. for(i=0; i<nstr; i++)
  539. str[i] = textreadc(t, q++);
  540. q = t->q0-nstr-npath;
  541. for(i=0; i<npath; i++)
  542. path[i] = textreadc(t, q++);
  543. /* is path rooted? if not, we need to make it relative to window path */
  544. if(npath>0 && path[0]=='/')
  545. dir = (Runestr){path, npath};
  546. else{
  547. dir = dirname(t, nil, 0);
  548. if(dir.nr + 1 + npath > nelem(tmp)){
  549. free(dir.r);
  550. goto Return;
  551. }
  552. if(dir.nr == 0){
  553. dir.nr = 1;
  554. dir.r = runestrdup(L".");
  555. }
  556. runemove(tmp, dir.r, dir.nr);
  557. tmp[dir.nr] = '/';
  558. runemove(tmp+dir.nr+1, path, npath);
  559. free(dir.r);
  560. dir.r = tmp;
  561. dir.nr += 1+npath;
  562. dir = cleanrname(dir);
  563. }
  564. s = smprint("%.*S", nstr, str);
  565. dirs = smprint("%.*S", dir.nr, dir.r);
  566. c = complete(dirs, s);
  567. free(s);
  568. if(c == nil){
  569. warning(nil, "error attempting completion: %r\n");
  570. goto Return;
  571. }
  572. if(!c->advance){
  573. warning(nil, "%.*S%s%.*S*%s\n",
  574. dir.nr, dir.r,
  575. dir.nr>0 && dir.r[dir.nr-1]!='/' ? "/" : "",
  576. nstr, str,
  577. c->nmatch? "" : ": no matches in:");
  578. for(i=0; i<c->nfile; i++)
  579. warning(nil, " %s\n", c->filename[i]);
  580. }
  581. if(c->advance)
  582. rp = runesmprint("%s", c->string);
  583. else
  584. rp = nil;
  585. Return:
  586. freecompletion(c);
  587. free(dirs);
  588. free(str);
  589. free(path);
  590. return rp;
  591. }
  592. void
  593. texttype(Text *t, Rune r)
  594. {
  595. uint q0, q1;
  596. int nnb, nb, n, i;
  597. int nr;
  598. Rune *rp;
  599. Text *u;
  600. if(t->what!=Body && r=='\n')
  601. return;
  602. nr = 1;
  603. rp = &r;
  604. switch(r){
  605. case Kleft:
  606. if(t->q0 > 0){
  607. wincommit(t->w, t);
  608. textshow(t, t->q0-1, t->q0-1, TRUE);
  609. }
  610. return;
  611. case Kright:
  612. if(t->q1 < t->file->nc){
  613. wincommit(t->w, t);
  614. textshow(t, t->q1+1, t->q1+1, TRUE);
  615. }
  616. return;
  617. case Kdown:
  618. n = t->maxlines/3;
  619. goto case_Down;
  620. case Kscrollonedown:
  621. n = mousescrollsize(t->maxlines);
  622. if(n <= 0)
  623. n = 1;
  624. goto case_Down;
  625. case Kpgdown:
  626. n = 2*t->maxlines/3;
  627. case_Down:
  628. q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height));
  629. textsetorigin(t, q0, TRUE);
  630. return;
  631. case Kup:
  632. n = t->maxlines/3;
  633. goto case_Up;
  634. case Kscrolloneup:
  635. n = mousescrollsize(t->maxlines);
  636. goto case_Up;
  637. case Kpgup:
  638. n = 2*t->maxlines/3;
  639. case_Up:
  640. q0 = textbacknl(t, t->org, n);
  641. textsetorigin(t, q0, TRUE);
  642. return;
  643. case Khome:
  644. textshow(t, 0, 0, FALSE);
  645. return;
  646. case Kend:
  647. if(t->w)
  648. wincommit(t->w, t);
  649. else
  650. textcommit(t, TRUE);
  651. textshow(t, t->file->nc, t->file->nc, FALSE);
  652. return;
  653. }
  654. if(t->what == Body){
  655. seq++;
  656. filemark(t->file);
  657. }
  658. if(t->q1 > t->q0){
  659. if(t->ncache != 0)
  660. error("text.type");
  661. cut(t, t, nil, TRUE, TRUE, nil, 0);
  662. t->eq0 = ~0;
  663. }
  664. textshow(t, t->q0, t->q0, 1);
  665. switch(r){
  666. case 0x06:
  667. case Kins:
  668. rp = textcomplete(t);
  669. if(rp == nil)
  670. return;
  671. nr = runestrlen(rp);
  672. break; /* fall through to normal insertion case */
  673. case 0x1B:
  674. if(t->eq0 != ~0)
  675. textsetselect(t, t->eq0, t->q0);
  676. if(t->ncache > 0){
  677. if(t->w != nil)
  678. wincommit(t->w, t);
  679. else
  680. textcommit(t, TRUE);
  681. }
  682. return;
  683. case 0x08: /* ^H: erase character */
  684. case 0x15: /* ^U: erase line */
  685. case 0x17: /* ^W: erase word */
  686. if(t->q0 == 0) /* nothing to erase */
  687. return;
  688. nnb = textbswidth(t, r);
  689. q1 = t->q0;
  690. q0 = q1-nnb;
  691. /* if selection is at beginning of window, avoid deleting invisible text */
  692. if(q0 < t->org){
  693. q0 = t->org;
  694. nnb = q1-q0;
  695. }
  696. if(nnb <= 0)
  697. return;
  698. for(i=0; i<t->file->ntext; i++){
  699. u = t->file->text[i];
  700. u->nofill = TRUE;
  701. nb = nnb;
  702. n = u->ncache;
  703. if(n > 0){
  704. if(q1 != u->cq0+n)
  705. error("text.type backspace");
  706. if(n > nb)
  707. n = nb;
  708. u->ncache -= n;
  709. textdelete(u, q1-n, q1, FALSE);
  710. nb -= n;
  711. }
  712. if(u->eq0==q1 || u->eq0==~0)
  713. u->eq0 = q0;
  714. if(nb && u==t)
  715. textdelete(u, q0, q0+nb, TRUE);
  716. if(u != t)
  717. textsetselect(u, u->q0, u->q1);
  718. else
  719. textsetselect(t, q0, q0);
  720. u->nofill = FALSE;
  721. }
  722. for(i=0; i<t->file->ntext; i++)
  723. textfill(t->file->text[i]);
  724. return;
  725. case '\n':
  726. if(t->w->autoindent){
  727. /* find beginning of previous line using backspace code */
  728. nnb = textbswidth(t, 0x15); /* ^U case */
  729. rp = runemalloc(nnb + 1);
  730. nr = 0;
  731. rp[nr++] = r;
  732. for(i=0; i<nnb; i++){
  733. r = textreadc(t, t->q0-nnb+i);
  734. if(r != ' ' && r != '\t')
  735. break;
  736. rp[nr++] = r;
  737. }
  738. }
  739. break; /* fall through to normal code */
  740. }
  741. /* otherwise ordinary character; just insert, typically in caches of all texts */
  742. for(i=0; i<t->file->ntext; i++){
  743. u = t->file->text[i];
  744. if(u->eq0 == ~0)
  745. u->eq0 = t->q0;
  746. if(u->ncache == 0)
  747. u->cq0 = t->q0;
  748. else if(t->q0 != u->cq0+u->ncache)
  749. error("text.type cq1");
  750. textinsert(u, t->q0, rp, nr, FALSE);
  751. if(u != t)
  752. textsetselect(u, u->q0, u->q1);
  753. if(u->ncache+nr > u->ncachealloc){
  754. u->ncachealloc += 10 + nr;
  755. u->cache = runerealloc(u->cache, u->ncachealloc);
  756. }
  757. runemove(u->cache+u->ncache, rp, nr);
  758. u->ncache += nr;
  759. }
  760. if(rp != &r)
  761. free(rp);
  762. textsetselect(t, t->q0+nr, t->q0+nr);
  763. if(r=='\n' && t->w!=nil)
  764. wincommit(t->w, t);
  765. }
  766. void
  767. textcommit(Text *t, int tofile)
  768. {
  769. if(t->ncache == 0)
  770. return;
  771. if(tofile)
  772. fileinsert(t->file, t->cq0, t->cache, t->ncache);
  773. if(t->what == Body){
  774. t->w->dirty = TRUE;
  775. t->w->utflastqid = -1;
  776. }
  777. t->ncache = 0;
  778. }
  779. static Text *clicktext;
  780. static uint clickmsec;
  781. static Text *selecttext;
  782. static uint selectq;
  783. /*
  784. * called from frame library
  785. */
  786. void
  787. framescroll(Frame *f, int dl)
  788. {
  789. if(f != &selecttext->Frame)
  790. error("frameselect not right frame");
  791. textframescroll(selecttext, dl);
  792. }
  793. void
  794. textframescroll(Text *t, int dl)
  795. {
  796. uint q0;
  797. if(dl == 0){
  798. scrsleep(100);
  799. return;
  800. }
  801. if(dl < 0){
  802. q0 = textbacknl(t, t->org, -dl);
  803. if(selectq > t->org+t->p0)
  804. textsetselect(t, t->org+t->p0, selectq);
  805. else
  806. textsetselect(t, selectq, t->org+t->p0);
  807. }else{
  808. if(t->org+t->nchars == t->file->nc)
  809. return;
  810. q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+dl*t->font->height));
  811. if(selectq > t->org+t->p1)
  812. textsetselect(t, t->org+t->p1, selectq);
  813. else
  814. textsetselect(t, selectq, t->org+t->p1);
  815. }
  816. textsetorigin(t, q0, TRUE);
  817. }
  818. void
  819. textselect(Text *t)
  820. {
  821. uint q0, q1;
  822. int b, x, y;
  823. int state, op;
  824. selecttext = t;
  825. /*
  826. * To have double-clicking and chording, we double-click
  827. * immediately if it might make sense.
  828. */
  829. b = mouse->buttons;
  830. q0 = t->q0;
  831. q1 = t->q1;
  832. selectq = t->org+frcharofpt(t, mouse->xy);
  833. if(clicktext==t && mouse->msec-clickmsec<500)
  834. if(q0==q1 && selectq==q0){
  835. textdoubleclick(t, &q0, &q1);
  836. textsetselect(t, q0, q1);
  837. flushimage(display, 1);
  838. x = mouse->xy.x;
  839. y = mouse->xy.y;
  840. /* stay here until something interesting happens */
  841. do
  842. readmouse(mousectl);
  843. while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3);
  844. mouse->xy.x = x; /* in case we're calling frselect */
  845. mouse->xy.y = y;
  846. q0 = t->q0; /* may have changed */
  847. q1 = t->q1;
  848. selectq = q0;
  849. }
  850. if(mouse->buttons == b){
  851. t->Frame.scroll = framescroll;
  852. frselect(t, mousectl);
  853. /* horrible botch: while asleep, may have lost selection altogether */
  854. if(selectq > t->file->nc)
  855. selectq = t->org + t->p0;
  856. t->Frame.scroll = nil;
  857. if(selectq < t->org)
  858. q0 = selectq;
  859. else
  860. q0 = t->org + t->p0;
  861. if(selectq > t->org+t->nchars)
  862. q1 = selectq;
  863. else
  864. q1 = t->org+t->p1;
  865. }
  866. if(q0 == q1){
  867. if(q0==t->q0 && clicktext==t && mouse->msec-clickmsec<500){
  868. textdoubleclick(t, &q0, &q1);
  869. clicktext = nil;
  870. }else{
  871. clicktext = t;
  872. clickmsec = mouse->msec;
  873. }
  874. }else
  875. clicktext = nil;
  876. textsetselect(t, q0, q1);
  877. flushimage(display, 1);
  878. state = op = 0; /* undo when possible; +1 for cut, -1 for paste */
  879. while(mouse->buttons){
  880. mouse->msec = 0;
  881. b = mouse->buttons;
  882. if(b & 6){
  883. if(state==0 && op==0 && t->what==Body){
  884. seq++;
  885. filemark(t->w->body.file);
  886. }
  887. if(b & 2){
  888. if(state==-1 && t->what==Body){
  889. winundo(t->w, TRUE);
  890. textsetselect(t, q0, t->q0);
  891. state = 0;
  892. }else if(state != 1 && op != -1){
  893. cut(t, t, nil, TRUE, TRUE, nil, 0);
  894. op = state = 1;
  895. }
  896. }else{
  897. if(state==1 && t->what==Body){
  898. winundo(t->w, TRUE);
  899. textsetselect(t, q0, t->q1);
  900. state = 0;
  901. }else if(state != -1 && op != 1){
  902. paste(t, t, nil, TRUE, FALSE, nil, 0);
  903. op = state = -1;
  904. }
  905. }
  906. textscrdraw(t);
  907. clearmouse();
  908. }
  909. flushimage(display, 1);
  910. while(mouse->buttons == b)
  911. readmouse(mousectl);
  912. clicktext = nil;
  913. }
  914. }
  915. void
  916. textshow(Text *t, uint q0, uint q1, int doselect)
  917. {
  918. int qe;
  919. int nl;
  920. uint q;
  921. if(t->what != Body){
  922. if(doselect)
  923. textsetselect(t, q0, q1);
  924. return;
  925. }
  926. if(t->w!=nil && t->maxlines==0)
  927. colgrow(t->col, t->w, 1);
  928. if(doselect)
  929. textsetselect(t, q0, q1);
  930. qe = t->org+t->nchars;
  931. if(t->org<=q0 && (q0<qe || (q0==qe && qe==t->file->nc+t->ncache)))
  932. textscrdraw(t);
  933. else{
  934. if(t->w->nopen[QWevent] > 0)
  935. nl = 3*t->maxlines/4;
  936. else
  937. nl = t->maxlines/4;
  938. q = textbacknl(t, q0, nl);
  939. /* avoid going backwards if trying to go forwards - long lines! */
  940. if(!(q0>t->org && q<t->org))
  941. textsetorigin(t, q, TRUE);
  942. while(q0 > t->org+t->nchars)
  943. textsetorigin(t, t->org+1, FALSE);
  944. }
  945. }
  946. static
  947. int
  948. region(int a, int b)
  949. {
  950. if(a < b)
  951. return -1;
  952. if(a == b)
  953. return 0;
  954. return 1;
  955. }
  956. void
  957. selrestore(Frame *f, Point pt0, uint p0, uint p1)
  958. {
  959. if(p1<=f->p0 || p0>=f->p1){
  960. /* no overlap */
  961. frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]);
  962. return;
  963. }
  964. if(p0>=f->p0 && p1<=f->p1){
  965. /* entirely inside */
  966. frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
  967. return;
  968. }
  969. /* they now are known to overlap */
  970. /* before selection */
  971. if(p0 < f->p0){
  972. frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]);
  973. p0 = f->p0;
  974. pt0 = frptofchar(f, p0);
  975. }
  976. /* after selection */
  977. if(p1 > f->p1){
  978. frdrawsel0(f, frptofchar(f, f->p1), f->p1, p1, f->cols[BACK], f->cols[TEXT]);
  979. p1 = f->p1;
  980. }
  981. /* inside selection */
  982. frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
  983. }
  984. void
  985. textsetselect(Text *t, uint q0, uint q1)
  986. {
  987. int p0, p1;
  988. /* t->p0 and t->p1 are always right; t->q0 and t->q1 may be off */
  989. t->q0 = q0;
  990. t->q1 = q1;
  991. /* compute desired p0,p1 from q0,q1 */
  992. p0 = q0-t->org;
  993. p1 = q1-t->org;
  994. if(p0 < 0)
  995. p0 = 0;
  996. if(p1 < 0)
  997. p1 = 0;
  998. if(p0 > t->nchars)
  999. p0 = t->nchars;
  1000. if(p1 > t->nchars)
  1001. p1 = t->nchars;
  1002. if(p0==t->p0 && p1==t->p1)
  1003. return;
  1004. /* screen disagrees with desired selection */
  1005. if(t->p1<=p0 || p1<=t->p0 || p0==p1 || t->p1==t->p0){
  1006. /* no overlap or too easy to bother trying */
  1007. frdrawsel(t, frptofchar(t, t->p0), t->p0, t->p1, 0);
  1008. frdrawsel(t, frptofchar(t, p0), p0, p1, 1);
  1009. goto Return;
  1010. }
  1011. /* overlap; avoid unnecessary painting */
  1012. if(p0 < t->p0){
  1013. /* extend selection backwards */
  1014. frdrawsel(t, frptofchar(t, p0), p0, t->p0, 1);
  1015. }else if(p0 > t->p0){
  1016. /* trim first part of selection */
  1017. frdrawsel(t, frptofchar(t, t->p0), t->p0, p0, 0);
  1018. }
  1019. if(p1 > t->p1){
  1020. /* extend selection forwards */
  1021. frdrawsel(t, frptofchar(t, t->p1), t->p1, p1, 1);
  1022. }else if(p1 < t->p1){
  1023. /* trim last part of selection */
  1024. frdrawsel(t, frptofchar(t, p1), p1, t->p1, 0);
  1025. }
  1026. Return:
  1027. t->p0 = p0;
  1028. t->p1 = p1;
  1029. }
  1030. /*
  1031. * Release the button in less than DELAY ms and it's considered a null selection
  1032. * if the mouse hardly moved, regardless of whether it crossed a char boundary.
  1033. */
  1034. enum {
  1035. DELAY = 2,
  1036. MINMOVE = 4,
  1037. };
  1038. uint
  1039. xselect(Frame *f, Mousectl *mc, Image *col, uint *p1p) /* when called, button is down */
  1040. {
  1041. uint p0, p1, q, tmp;
  1042. ulong msec;
  1043. Point mp, pt0, pt1, qt;
  1044. int reg, b;
  1045. mp = mc->xy;
  1046. b = mc->buttons;
  1047. msec = mc->msec;
  1048. /* remove tick */
  1049. if(f->p0 == f->p1)
  1050. frtick(f, frptofchar(f, f->p0), 0);
  1051. p0 = p1 = frcharofpt(f, mp);
  1052. pt0 = frptofchar(f, p0);
  1053. pt1 = frptofchar(f, p1);
  1054. reg = 0;
  1055. frtick(f, pt0, 1);
  1056. do{
  1057. q = frcharofpt(f, mc->xy);
  1058. if(p1 != q){
  1059. if(p0 == p1)
  1060. frtick(f, pt0, 0);
  1061. if(reg != region(q, p0)){ /* crossed starting point; reset */
  1062. if(reg > 0)
  1063. selrestore(f, pt0, p0, p1);
  1064. else if(reg < 0)
  1065. selrestore(f, pt1, p1, p0);
  1066. p1 = p0;
  1067. pt1 = pt0;
  1068. reg = region(q, p0);
  1069. if(reg == 0)
  1070. frdrawsel0(f, pt0, p0, p1, col, display->white);
  1071. }
  1072. qt = frptofchar(f, q);
  1073. if(reg > 0){
  1074. if(q > p1)
  1075. frdrawsel0(f, pt1, p1, q, col, display->white);
  1076. else if(q < p1)
  1077. selrestore(f, qt, q, p1);
  1078. }else if(reg < 0){
  1079. if(q > p1)
  1080. selrestore(f, pt1, p1, q);
  1081. else
  1082. frdrawsel0(f, qt, q, p1, col, display->white);
  1083. }
  1084. p1 = q;
  1085. pt1 = qt;
  1086. }
  1087. if(p0 == p1)
  1088. frtick(f, pt0, 1);
  1089. flushimage(f->display, 1);
  1090. readmouse(mc);
  1091. }while(mc->buttons == b);
  1092. if(mc->msec-msec < DELAY && p0!=p1
  1093. && abs(mp.x-mc->xy.x)<MINMOVE
  1094. && abs(mp.y-mc->xy.y)<MINMOVE) {
  1095. if(reg > 0)
  1096. selrestore(f, pt0, p0, p1);
  1097. else if(reg < 0)
  1098. selrestore(f, pt1, p1, p0);
  1099. p1 = p0;
  1100. }
  1101. if(p1 < p0){
  1102. tmp = p0;
  1103. p0 = p1;
  1104. p1 = tmp;
  1105. }
  1106. pt0 = frptofchar(f, p0);
  1107. if(p0 == p1)
  1108. frtick(f, pt0, 0);
  1109. selrestore(f, pt0, p0, p1);
  1110. /* restore tick */
  1111. if(f->p0 == f->p1)
  1112. frtick(f, frptofchar(f, f->p0), 1);
  1113. flushimage(f->display, 1);
  1114. *p1p = p1;
  1115. return p0;
  1116. }
  1117. int
  1118. textselect23(Text *t, uint *q0, uint *q1, Image *high, int mask)
  1119. {
  1120. uint p0, p1;
  1121. int buts;
  1122. p0 = xselect(t, mousectl, high, &p1);
  1123. buts = mousectl->buttons;
  1124. if((buts & mask) == 0){
  1125. *q0 = p0+t->org;
  1126. *q1 = p1+t->org;
  1127. }
  1128. while(mousectl->buttons)
  1129. readmouse(mousectl);
  1130. return buts;
  1131. }
  1132. int
  1133. textselect2(Text *t, uint *q0, uint *q1, Text **tp)
  1134. {
  1135. int buts;
  1136. *tp = nil;
  1137. buts = textselect23(t, q0, q1, but2col, 4);
  1138. if(buts & 4)
  1139. return 0;
  1140. if(buts & 1){ /* pick up argument */
  1141. *tp = argtext;
  1142. return 1;
  1143. }
  1144. return 1;
  1145. }
  1146. int
  1147. textselect3(Text *t, uint *q0, uint *q1)
  1148. {
  1149. int h;
  1150. h = (textselect23(t, q0, q1, but3col, 1|2) == 0);
  1151. return h;
  1152. }
  1153. static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 };
  1154. static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
  1155. static Rune left2[] = { L'\n', 0 };
  1156. static Rune left3[] = { L'\'', L'"', L'`', 0 };
  1157. static
  1158. Rune *left[] = {
  1159. left1,
  1160. left2,
  1161. left3,
  1162. nil
  1163. };
  1164. static
  1165. Rune *right[] = {
  1166. right1,
  1167. left2,
  1168. left3,
  1169. nil
  1170. };
  1171. void
  1172. textdoubleclick(Text *t, uint *q0, uint *q1)
  1173. {
  1174. int c, i;
  1175. Rune *r, *l, *p;
  1176. uint q;
  1177. for(i=0; left[i]!=nil; i++){
  1178. q = *q0;
  1179. l = left[i];
  1180. r = right[i];
  1181. /* try matching character to left, looking right */
  1182. if(q == 0)
  1183. c = '\n';
  1184. else
  1185. c = textreadc(t, q-1);
  1186. p = runestrchr(l, c);
  1187. if(p != nil){
  1188. if(textclickmatch(t, c, r[p-l], 1, &q))
  1189. *q1 = q-(c!='\n');
  1190. return;
  1191. }
  1192. /* try matching character to right, looking left */
  1193. if(q == t->file->nc)
  1194. c = '\n';
  1195. else
  1196. c = textreadc(t, q);
  1197. p = runestrchr(r, c);
  1198. if(p != nil){
  1199. if(textclickmatch(t, c, l[p-r], -1, &q)){
  1200. *q1 = *q0+(*q0<t->file->nc && c=='\n');
  1201. *q0 = q;
  1202. if(c!='\n' || q!=0 || textreadc(t, 0)=='\n')
  1203. (*q0)++;
  1204. }
  1205. return;
  1206. }
  1207. }
  1208. /* try filling out word to right */
  1209. while(*q1<t->file->nc && isalnum(textreadc(t, *q1)))
  1210. (*q1)++;
  1211. /* try filling out word to left */
  1212. while(*q0>0 && isalnum(textreadc(t, *q0-1)))
  1213. (*q0)--;
  1214. }
  1215. int
  1216. textclickmatch(Text *t, int cl, int cr, int dir, uint *q)
  1217. {
  1218. Rune c;
  1219. int nest;
  1220. nest = 1;
  1221. for(;;){
  1222. if(dir > 0){
  1223. if(*q == t->file->nc)
  1224. break;
  1225. c = textreadc(t, *q);
  1226. (*q)++;
  1227. }else{
  1228. if(*q == 0)
  1229. break;
  1230. (*q)--;
  1231. c = textreadc(t, *q);
  1232. }
  1233. if(c == cr){
  1234. if(--nest==0)
  1235. return 1;
  1236. }else if(c == cl)
  1237. nest++;
  1238. }
  1239. return cl=='\n' && nest==1;
  1240. }
  1241. uint
  1242. textbacknl(Text *t, uint p, uint n)
  1243. {
  1244. int i, j;
  1245. /* look for start of this line if n==0 */
  1246. if(n==0 && p>0 && textreadc(t, p-1)!='\n')
  1247. n = 1;
  1248. i = n;
  1249. while(i-->0 && p>0){
  1250. --p; /* it's at a newline now; back over it */
  1251. if(p == 0)
  1252. break;
  1253. /* at 128 chars, call it a line anyway */
  1254. for(j=128; --j>0 && p>0; p--)
  1255. if(textreadc(t, p-1)=='\n')
  1256. break;
  1257. }
  1258. return p;
  1259. }
  1260. void
  1261. textsetorigin(Text *t, uint org, int exact)
  1262. {
  1263. int i, a, fixup;
  1264. Rune *r;
  1265. uint n;
  1266. if(org>0 && !exact){
  1267. /* org is an estimate of the char posn; find a newline */
  1268. /* don't try harder than 256 chars */
  1269. for(i=0; i<256 && org<t->file->nc; i++){
  1270. if(textreadc(t, org) == '\n'){
  1271. org++;
  1272. break;
  1273. }
  1274. org++;
  1275. }
  1276. }
  1277. a = org-t->org;
  1278. fixup = 0;
  1279. if(a>=0 && a<t->nchars){
  1280. frdelete(t, 0, a);
  1281. fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
  1282. }
  1283. else if(a<0 && -a<t->nchars){
  1284. n = t->org - org;
  1285. r = runemalloc(n);
  1286. bufread(t->file, org, r, n);
  1287. frinsert(t, r, r+n, 0);
  1288. free(r);
  1289. }else
  1290. frdelete(t, 0, t->nchars);
  1291. t->org = org;
  1292. textfill(t);
  1293. textscrdraw(t);
  1294. textsetselect(t, t->q0, t->q1);
  1295. if(fixup && t->p1 > t->p0)
  1296. frdrawsel(t, frptofchar(t, t->p1-1), t->p1-1, t->p1, 1);
  1297. }
  1298. void
  1299. textreset(Text *t)
  1300. {
  1301. t->file->seq = 0;
  1302. t->eq0 = ~0;
  1303. /* do t->delete(0, t->nc, TRUE) without building backup stuff */
  1304. textsetselect(t, t->org, t->org);
  1305. frdelete(t, 0, t->nchars);
  1306. t->org = 0;
  1307. t->q0 = 0;
  1308. t->q1 = 0;
  1309. filereset(t->file);
  1310. bufreset(t->file);
  1311. }