tindx.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. #include "lib9.h"
  2. #include "draw.h"
  3. #include "tk.h"
  4. #include "textw.h"
  5. #define istring u.string
  6. #define iwin u.win
  7. #define imark u.mark
  8. #define iline u.line
  9. /* debugging */
  10. extern int tktdbg;
  11. extern void tktprinttext(TkText*);
  12. extern void tktprintindex(TkTindex*);
  13. extern void tktprintitem(TkTitem*);
  14. extern void tktprintline(TkTline*);
  15. char*
  16. tktindparse(Tk *tk, char **pspec, TkTindex *ans)
  17. {
  18. int m, n, done, neg, modstart;
  19. char *s, *mod;
  20. TkTline *lend;
  21. TkText *tkt;
  22. char *buf;
  23. buf = mallocz(Tkmaxitem, 0);
  24. if(buf == nil)
  25. return TkNomem;
  26. tkt = TKobj(TkText, tk);
  27. lend = &tkt->end;
  28. *pspec = tkword(tk->env->top, *pspec, buf, buf+Tkmaxitem, nil);
  29. modstart = 0;
  30. for(mod = buf; *mod != '\0'; mod++)
  31. if(*mod == ' ' || *mod == '-' || *mod == '+') {
  32. modstart = *mod;
  33. *mod = '\0';
  34. break;
  35. }
  36. /*
  37. * XXX there's a problem here - if either coordinate is negative
  38. * which shouldn't be precluded, then the above scanning code
  39. * will break up the coordinate pair, so @-23,45 for example
  40. * yields a bad index, when it should probably return the index
  41. * of the character at the start of the line containing y=45.
  42. * i've seen this cause wm/sh to crash.
  43. */
  44. if(strcmp(buf, "end") == 0)
  45. tktendind(tkt, ans);
  46. else
  47. if(*buf == '@') {
  48. /* by coordinates */
  49. s = strchr(buf, ',');
  50. if(s == nil) {
  51. free(buf);
  52. return TkBadix;
  53. }
  54. *s = '\0';
  55. m = atoi(buf+1);
  56. n = atoi(s+1);
  57. tktxyind(tk, m, n, ans);
  58. }
  59. else
  60. if(*buf >= '0' && *buf <= '9') {
  61. /* line.char */
  62. s = strchr(buf, '.');
  63. if(s == nil) {
  64. free(buf);
  65. return TkBadix;
  66. }
  67. *s = '\0';
  68. m = atoi(buf);
  69. n = atoi(s+1);
  70. if(m < 1)
  71. m = 1;
  72. tktstartind(tkt, ans);
  73. while(--m > 0 && ans->line->next != lend)
  74. tktadjustind(tkt, TkTbyline, ans);
  75. while(n-- > 0 && ans->item->kind != TkTnewline)
  76. tktadjustind(tkt, TkTbychar, ans);
  77. }
  78. else
  79. if(*buf == '.') {
  80. /* window */
  81. tktstartind(tkt, ans);
  82. while(ans->line != lend) {
  83. if(ans->item->kind == TkTwin &&
  84. ans->item->iwin->sub != nil &&
  85. ans->item->iwin->sub->name != nil &&
  86. strcmp(ans->item->iwin->sub->name->name, buf) == 0)
  87. break;
  88. if(!tktadjustind(tkt, TkTbyitem, ans))
  89. ans->line = lend;
  90. }
  91. if(ans->line == lend) {
  92. free(buf);
  93. return TkBadix;
  94. }
  95. }
  96. else {
  97. s = strchr(buf, '.');
  98. if(s == nil) {
  99. if(tktmarkind(tk, buf, ans) == 0) {
  100. free(buf);
  101. return TkBadix;
  102. }
  103. }
  104. else {
  105. /* tag.first or tag.last */
  106. *s = '\0';
  107. if(strcmp(s+1, "first") == 0) {
  108. if(tkttagind(tk, buf, 1, ans) == 0) {
  109. free(buf);
  110. return TkBadix;
  111. }
  112. }
  113. else
  114. if(strcmp(s+1, "last") == 0) {
  115. if(tkttagind(tk, buf, 0, ans) == 0) {
  116. free(buf);
  117. return TkBadix;
  118. }
  119. }
  120. else {
  121. free(buf);
  122. return TkBadix;
  123. }
  124. }
  125. }
  126. if(modstart == 0) {
  127. free(buf);
  128. return nil;
  129. }
  130. *mod = modstart;
  131. while(*mod == ' ')
  132. mod++;
  133. while(*mod != '\0') {
  134. done = 0;
  135. switch(*mod) {
  136. case '+':
  137. case '-':
  138. neg = (*mod == '-');
  139. mod++;
  140. while(*mod == ' ')
  141. mod++;
  142. n = strtol(mod, &mod, 10);
  143. while(*mod == ' ')
  144. mod++;
  145. while(n-- > 0) {
  146. if(*mod == 'c')
  147. tktadjustind(tkt, neg? TkTbycharback : TkTbychar, ans);
  148. else
  149. if(*mod == 'l')
  150. tktadjustind(tkt, neg? TkTbylineback : TkTbyline, ans);
  151. else
  152. done = 1;
  153. }
  154. break;
  155. case 'l':
  156. if(strncmp(mod, "lines", 5) == 0)
  157. tktadjustind(tkt, TkTbylinestart, ans);
  158. else
  159. if(strncmp(mod, "linee", 5) == 0)
  160. tktadjustind(tkt, TkTbylineend, ans);
  161. else
  162. done = 1;
  163. break;
  164. case 'w':
  165. if(strncmp(mod, "words", 5) == 0)
  166. tktadjustind(tkt, TkTbywordstart, ans);
  167. else
  168. if(strncmp(mod, "worde", 5) == 0)
  169. tktadjustind(tkt, TkTbywordend, ans);
  170. else
  171. done = 1;
  172. break;
  173. default:
  174. done = 1;
  175. }
  176. if(done)
  177. break;
  178. while(tkiswordchar(*mod))
  179. mod++;
  180. while(*mod == ' ')
  181. mod++;
  182. }
  183. free(buf);
  184. return nil;
  185. }
  186. int
  187. tktisbreak(int c)
  188. {
  189. /* unicode rules suggest / as well but that would split dates, and URLs might as well char. wrap */
  190. return c == ' ' || c == '\t' || c == '\n' || c == '-' || c == ',';
  191. /* previously included . but would probably need more then to handle ." */
  192. }
  193. /*
  194. * Adjust the index p by units (one of TkTbyitem, etc.).
  195. * The TkTbychar units mean that the final point should rest on a
  196. * "character" (in text widget index space; i.e., a newline, a rune,
  197. * and an embedded window are each 1 character, but marks and contlines are not).
  198. *
  199. * Indexes may not point in the tkt->start or tkt->end lines (which have
  200. * no items); tktadjustind sticks at the beginning or end of the buffer.
  201. *
  202. * Return 1 if the index changes at all, 0 otherwise.
  203. */
  204. int
  205. tktadjustind(TkText *tkt, int units, TkTindex *p)
  206. {
  207. int n, opos, count, c;
  208. TkTitem *i, *it, *oit;
  209. TkTindex q;
  210. oit = p->item;
  211. opos = p->pos;
  212. count = 1;
  213. switch(units) {
  214. case TkTbyitemback:
  215. it = p->item;
  216. p->item = p->line->items;
  217. p->pos = 0;
  218. if(it == p->item) {
  219. if(p->line->prev != &tkt->start) {
  220. p->line = p->line->prev;
  221. p->item = tktlastitem(p->line->items);
  222. }
  223. }
  224. else {
  225. while(p->item->next != it) {
  226. p->item = p->item->next;
  227. if(tktdbg && p->item == nil) {
  228. print("tktadjustind: botch 1\n");
  229. break;
  230. }
  231. }
  232. }
  233. break;
  234. case TkTbyitem:
  235. p->pos = 0;
  236. i = p->item->next;
  237. if(i == nil) {
  238. if(p->line->next != &tkt->end) {
  239. p->line = p->line->next;
  240. p->item = p->line->items;
  241. }
  242. }
  243. else
  244. p->item = i;
  245. break;
  246. case TkTbytlineback:
  247. if(p->line->prev != &tkt->start)
  248. p->line = p->line->prev;
  249. p->item = p->line->items;
  250. p->pos = 0;
  251. break;
  252. case TkTbytline:
  253. if(p->line->next != &tkt->end)
  254. p->line = p->line->next;
  255. p->item = p->line->items;
  256. p->pos = 0;
  257. break;
  258. case TkTbycharstart:
  259. count = 0;
  260. case TkTbychar:
  261. while(count > 0) {
  262. i = p->item;
  263. n = tktposcount(i) - p->pos;
  264. if(count >= n) {
  265. if(tktadjustind(tkt, TkTbyitem, p))
  266. count -= n;
  267. else
  268. break;
  269. }
  270. else {
  271. p->pos += count;
  272. break;
  273. }
  274. }
  275. while(p->item->kind == TkTmark || p->item->kind == TkTcontline)
  276. if(!tktadjustind(tkt, TkTbyitem, p))
  277. break;
  278. break;
  279. case TkTbycharback:
  280. count = -1;
  281. while(count < 0) {
  282. if(p->pos + count >= 0) {
  283. p->pos += count;
  284. count = 0;
  285. }
  286. else {
  287. count += p->pos;
  288. if(!tktadjustind(tkt, TkTbyitemback, p))
  289. break;
  290. n = tktposcount(p->item);
  291. p->pos = n;
  292. }
  293. }
  294. break;
  295. case TkTbylineback:
  296. count = -1;
  297. /* fall through */
  298. case TkTbyline:
  299. n = tktlinepos(tkt, p);
  300. while(count > 0) {
  301. if(p->line->next == &tkt->end) {
  302. count = 0;
  303. break;
  304. }
  305. if(p->line->flags&TkTlast)
  306. count--;
  307. p->line = p->line->next;
  308. }
  309. while(count < 0 && p->line->prev != &tkt->start) {
  310. if(p->line->flags&TkTfirst)
  311. count++;
  312. p->line = p->line->prev;
  313. }
  314. tktadjustind(tkt, TkTbylinestart, p);
  315. while(n > 0) {
  316. if(p->item->kind == TkTnewline)
  317. break;
  318. if(!tktadjustind(tkt, TkTbychar, p))
  319. break;
  320. n--;
  321. }
  322. break;
  323. case TkTbylinestart:
  324. /* note: can call this with only p->line set correctly in *p */
  325. while(!(p->line->flags&TkTfirst))
  326. p->line = p->line->prev;
  327. p->item = p->line->items;
  328. p->pos = 0;
  329. break;
  330. case TkTbylineend:
  331. while(p->item->kind != TkTnewline)
  332. if(!tktadjustind(tkt, TkTbychar, p))
  333. break;
  334. break;
  335. case TkTbywordstart:
  336. tktadjustind(tkt, TkTbycharstart, p);
  337. q = *p;
  338. c = tktindrune(p);
  339. while(tkiswordchar(c)) {
  340. q = *p;
  341. if(!tktadjustind(tkt, TkTbycharback, p))
  342. break;
  343. c = tktindrune(p);
  344. }
  345. *p = q;
  346. break;
  347. case TkTbywordend:
  348. tktadjustind(tkt, TkTbycharstart, p);
  349. if(p->item->kind == TkTascii || p->item->kind == TkTrune) {
  350. c = tktindrune(p);
  351. if(tkiswordchar(c)) {
  352. do {
  353. if(!tktadjustind(tkt, TkTbychar, p))
  354. break;
  355. c = tktindrune(p);
  356. } while(tkiswordchar(c));
  357. }
  358. else
  359. tktadjustind(tkt, TkTbychar, p);
  360. }
  361. else if(!(p->item->kind == TkTnewline && p->line->next == &tkt->end))
  362. tktadjustind(tkt, TkTbychar, p);
  363. break;
  364. case TkTbywrapstart:
  365. tktadjustind(tkt, TkTbycharstart, p);
  366. q = *p;
  367. c = tktindrune(p);
  368. while(!tktisbreak(c)) {
  369. q = *p;
  370. if(!tktadjustind(tkt, TkTbycharback, p))
  371. break;
  372. c = tktindrune(p);
  373. }
  374. *p = q;
  375. break;
  376. case TkTbywrapend:
  377. tktadjustind(tkt, TkTbycharstart, p);
  378. if(p->item->kind == TkTascii || p->item->kind == TkTrune) {
  379. c = tktindrune(p);
  380. if(!tktisbreak(c)) {
  381. do {
  382. if(!tktadjustind(tkt, TkTbychar, p))
  383. break;
  384. c = tktindrune(p);
  385. } while(!tktisbreak(c) && (p->item->kind == TkTascii || p->item->kind == TkTrune));
  386. while(tktisbreak(c) && tktadjustind(tkt, TkTbychar, p))
  387. c = tktindrune(p); /* could limit it */
  388. }
  389. else
  390. tktadjustind(tkt, TkTbychar, p);
  391. }
  392. else if(!(p->item->kind == TkTnewline && p->line->next == &tkt->end))
  393. tktadjustind(tkt, TkTbychar, p);
  394. break;
  395. }
  396. return (p->item != oit || p->pos != opos);
  397. }
  398. /* return 1 if advancing i1 by item eventually hits i2 */
  399. int
  400. tktindbefore(TkTindex *i1, TkTindex *i2)
  401. {
  402. int ans;
  403. TkTitem *i;
  404. TkTline *l1, *l2;
  405. ans = 0;
  406. l1 = i1->line;
  407. l2 = i2->line;
  408. if(l1 == l2) {
  409. if(i1->item == i2->item)
  410. ans = (i1->pos < i2->pos);
  411. else {
  412. for(i = i1->item; i != nil; i = i->next)
  413. if(i->next == i2->item) {
  414. ans = 1;
  415. break;
  416. }
  417. }
  418. }
  419. else {
  420. if(l1->orig.y < l2->orig.y)
  421. ans = 1;
  422. else
  423. if(l1->orig.y == l2->orig.y) {
  424. for(; l1 != nil; l1 = l1->next) {
  425. if(l1->next == l2) {
  426. ans = 1;
  427. break;
  428. }
  429. if(l1->orig.y > l2->orig.y)
  430. break;
  431. }
  432. }
  433. }
  434. return ans;
  435. }
  436. /*
  437. * This comparison only cares which characters the indices are before.
  438. * So two marks should be called "equal" (and not "less" or "greater")
  439. * if they are adjacent.
  440. */
  441. int
  442. tktindcompare(TkText *tkt, TkTindex *i1, int op, TkTindex *i2)
  443. {
  444. int eq, ans;
  445. TkTindex x1, x2;
  446. x1 = *i1;
  447. x2 = *i2;
  448. /* skip over any marks, contlines, to see if on same character */
  449. tktadjustind(tkt, TkTbycharstart, &x1);
  450. tktadjustind(tkt, TkTbycharstart, &x2);
  451. eq = (x1.item == x2.item && x1.pos == x2.pos);
  452. switch(op) {
  453. case TkEq:
  454. ans = eq;
  455. break;
  456. case TkNeq:
  457. ans = !eq;
  458. break;
  459. case TkLte:
  460. ans = eq || tktindbefore(i1, i2);
  461. break;
  462. case TkLt:
  463. ans = !eq && tktindbefore(i1, i2);
  464. break;
  465. case TkGte:
  466. ans = eq || tktindbefore(i2, i1);
  467. break;
  468. case TkGt:
  469. ans = !eq && tktindbefore(i2, i1);
  470. break;
  471. default:
  472. ans = 0; /* not reached */
  473. break;
  474. };
  475. return ans;
  476. }
  477. void
  478. tktstartind(TkText *tkt, TkTindex *ans)
  479. {
  480. ans->line = tkt->start.next;
  481. ans->item = ans->line->items;
  482. ans->pos = 0;
  483. }
  484. void
  485. tktendind(TkText *tkt, TkTindex *ans)
  486. {
  487. ans->line = tkt->end.prev;
  488. ans->item = tktlastitem(ans->line->items);
  489. ans->pos = 0;
  490. }
  491. void
  492. tktitemind(TkTitem *it, TkTindex *ans)
  493. {
  494. ans->item = it;
  495. ans->line = tktitemline(it);
  496. ans->pos = 0;
  497. }
  498. /*
  499. * Fill ans with the item that (x,y) (in V space) is over.
  500. * Return 0 if it is over the first half of the width,
  501. * and 1 if it is over the second half.
  502. */
  503. int
  504. tktxyind(Tk *tk, int x, int y, TkTindex *ans)
  505. {
  506. int n, w, secondhalf, k;
  507. Point p, q;
  508. TkTitem *i;
  509. TkText *tkt;
  510. tkt = TKobj(TkText, tk);
  511. tktstartind(tkt, ans);
  512. secondhalf = 0;
  513. /* (x,y), p, q in V space */
  514. p = subpt(ans->line->orig, tkt->deltatv);
  515. q = subpt(ans->line->next->orig, tkt->deltatv);
  516. while(ans->line->next != &tkt->end) {
  517. if(q.y > y)
  518. break;
  519. tktadjustind(tkt, TkTbytline, ans);
  520. p = q;
  521. q = subpt(ans->line->next->orig, tkt->deltatv);
  522. }
  523. if (ans->line->next == &tkt->end) {
  524. Point ep = subpt(tkt->end.orig, tkt->deltatv);
  525. if (ep.y < y)
  526. x = 1000000;
  527. }
  528. while(ans->item->next != nil) {
  529. i = ans->item;
  530. w = i->width;
  531. if(p.x+w > x) {
  532. n = tktposcount(i);
  533. if(n > 1) {
  534. for(k = 0; k < n; k++) {
  535. /* probably wrong w.r.t tag tabs */
  536. w = tktdispwidth(tk, nil, i, nil, p.x, k, 1);
  537. if(p.x+w > x) {
  538. ans->pos = k;
  539. break;
  540. }
  541. p.x += w;
  542. }
  543. }
  544. secondhalf = (p.x + w/2 <= x);
  545. break;
  546. }
  547. p.x += w;
  548. if(!tktadjustind(tkt, TkTbyitem, ans))
  549. break;
  550. }
  551. tktadjustind(tkt, TkTbycharstart, ans);
  552. return secondhalf;
  553. }