text.c 27 KB

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