1 #include "lib9.h"
2 #include "draw.h"
3 #include "tk.h"
4 #include "textw.h"
5
6 #define istring u.string
7 #define iwin u.win
8 #define imark u.mark
9 #define iline u.line
10
11 /* debugging */
12 int tktdbg;
13 extern void tktprinttext(TkText*);
14 extern void tktprintindex(TkTindex*);
15 extern void tktprintitem(TkTitem*);
16 extern void tktprintline(TkTline*);
17 extern void tktcheck(TkText*, char*);
18 int tktutfpos(char*, int);
19
20 char*
tktnewitem(int kind,int tagextra,TkTitem ** ret)21 tktnewitem(int kind, int tagextra,TkTitem **ret)
22 {
23 int n;
24 TkTitem *i;
25
26 n = sizeof(TkTitem) + tagextra * sizeof(ulong);
27 i = malloc(n);
28 if(i == nil)
29 return TkNomem;
30
31 memset(i, 0, n);
32 i->kind = kind;
33 i->tagextra = tagextra;
34 *ret = i;
35 return nil;
36 }
37
38 char*
tktnewline(int flags,TkTitem * items,TkTline * prev,TkTline * next,TkTline ** ret)39 tktnewline(int flags, TkTitem *items, TkTline *prev, TkTline *next, TkTline **ret)
40 {
41 TkTline *l;
42 TkTitem *i;
43
44 l = malloc(sizeof(TkTline));
45 if(l == nil)
46 return TkNomem;
47
48 memset(l, 0, sizeof(TkTline));
49 l->flags = flags;
50 l->items = items;
51 l->prev = prev;
52 l->next = next;
53 next->prev = l;
54 prev->next = l;
55
56 for(i = items; i->next != nil;)
57 i = i->next;
58 if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline))
59 print("text:tktnewline botch\n");
60 i->iline = l;
61
62 *ret = l;
63 return nil;
64 }
65
66 /*
67 * free items; freewins is 0 when the subwindows will be
68 * freed anyway as the main text widget is being destroyed.
69 */
70 void
tktfreeitems(TkText * tkt,TkTitem * i,int freewins)71 tktfreeitems(TkText *tkt, TkTitem *i, int freewins)
72 {
73 TkTitem *n;
74 Tk *tk;
75
76 while(i != nil) {
77 n = i->next;
78 if(tkt->mouse == i)
79 tkt->mouse = nil;
80 switch(i->kind) {
81 case TkTascii:
82 case TkTrune:
83 if(i->istring != nil)
84 free(i->istring);
85 break;
86 case TkTwin:
87 if (i->iwin != nil) {
88 tk = i->iwin->sub;
89 if (tk != nil) {
90 tk->geom = nil;
91 tk->destroyed = nil;
92 if (i->iwin->owned && freewins) {
93 if (tk->name != nil)
94 tkdestroy(tk->env->top, tk->name->name, nil);
95 } else {
96 tk->parent = nil;
97 tk->geom = nil;
98 tk->destroyed = nil;
99 }
100 }
101 if(i->iwin->create != nil)
102 free(i->iwin->create);
103 free(i->iwin);
104 }
105 break;
106 case TkTmark:
107 break;
108 }
109 free(i);
110 i = n;
111 }
112 }
113
114 void
tktfreelines(TkText * tkt,TkTline * l,int freewins)115 tktfreelines(TkText *tkt, TkTline *l, int freewins)
116 {
117 TkTline *n;
118
119 while(l != nil) {
120 n = l->next;
121 tktfreeitems(tkt, l->items, freewins);
122 free(l);
123 l = n;
124 }
125 }
126
127 void
tktfreetabs(TkTtabstop * t)128 tktfreetabs(TkTtabstop *t)
129 {
130 TkTtabstop *n;
131
132 while(t != nil) {
133 n = t->next;
134 free(t);
135 t = n;
136 }
137 }
138
139 void
tkfreetext(Tk * tk)140 tkfreetext(Tk *tk)
141 {
142 TkText *tkt = TKobj(TkText, tk);
143
144 if(tkt->start.next != nil && tkt->start.next != &(tkt->end)) {
145 tkt->end.prev->next = nil;
146 tktfreelines(tkt, tkt->start.next, 0);
147 }
148 tktfreeitems(tkt, tkt->start.items, 0);
149 tktfreeitems(tkt, tkt->end.items, 0);
150 tktfreetabs(tkt->tabs);
151 if(tkt->tagshare == nil)
152 tktfreetags(tkt->tags);
153 else
154 tk->binds = nil;
155 tktfreemarks(tkt->marks);
156 if(tkt->xscroll != nil)
157 free(tkt->xscroll);
158 if(tkt->yscroll != nil)
159 free(tkt->yscroll);
160 /* don't free image because it belongs to window */
161 }
162
163 /*
164 * Remove the item at ix, joining previous and next items.
165 * If item is at end of line, remove next line and join
166 * its items to this one (except at end).
167 * On return, ix is adjusted to point to the next item.
168 */
169 void
tktremitem(TkText * tkt,TkTindex * ix)170 tktremitem(TkText *tkt, TkTindex *ix)
171 {
172 TkTline *l, *lnext;
173 TkTindex prev, nx;
174 TkTitem *i, *ilast;
175
176 l = ix->line;
177 i = ix->item;
178
179 if(i->next == nil) {
180 if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline)) {
181 print("tktremitem: botch 1\n");
182 return;
183 }
184 lnext = l->next;
185 if(lnext == &tkt->end)
186 /* not supposed to remove final newline */
187 return;
188 if(i->kind == TkTnewline)
189 tkt->nlines--;
190 ilast = tktlastitem(lnext->items);
191 ilast->iline = l;
192 i->next = lnext->items;
193 l->flags = (l->flags & ~TkTlast) | (lnext->flags & TkTlast);
194 l->next = lnext->next;
195 lnext->next->prev = l;
196 free(lnext);
197 }
198 if(l->items == i)
199 l->items = i->next;
200 else {
201 prev = *ix;
202 if(!tktadjustind(tkt, TkTbyitemback, &prev) && tktdbg) {
203 print("tktremitem: botch 2\n");
204 return;
205 }
206 prev.item->next = i->next;
207 }
208 ix->item = i->next;
209 ix->pos = 0;
210 i->next = nil;
211 nx = *ix;
212 tktadjustind(tkt, TkTbycharstart, &nx);
213
214 /* check against cached items */
215 if(tkt->selfirst == i)
216 tkt->selfirst = nx.item;
217 if(tkt->sellast == i)
218 tkt->sellast = nx.item;
219 if(tkt->selfirst == tkt->sellast) {
220 tkt->selfirst = nil;
221 tkt->sellast = nil;
222 }
223
224 tktfreeitems(tkt, i, 1);
225 }
226
227 int
tktdispwidth(Tk * tk,TkTtabstop * tb,TkTitem * i,Font * f,int x,int pos,int nchars)228 tktdispwidth(Tk *tk, TkTtabstop *tb, TkTitem *i, Font *f, int x, int pos, int nchars)
229 {
230 int w, del, locked;
231 TkTtabstop *tbprev;
232 Display *d;
233 TkText *tkt;
234 TkEnv env;
235
236 tkt = TKobj(TkText, tk);
237 d = tk->env->top->display;
238 if (tb == nil)
239 tb = tkt->tabs;
240
241 switch(i->kind) {
242 case TkTrune:
243 pos = tktutfpos(i->istring, pos);
244 /* FALLTHRU */
245 case TkTascii:
246 if(f == nil) {
247 if(!tktanytags(i))
248 f = tk->env->font;
249 else {
250 tkttagopts(tk, i, nil, &env, nil, 1);
251 f = env.font;
252 }
253 }
254 locked = 0;
255 if(!(tkt->tflag&TkTdlocked))
256 locked = lockdisplay(d);
257 if(nchars >= 0)
258 w = stringnwidth(f, i->istring+pos, nchars);
259 else
260 w = stringwidth(f, i->istring+pos);
261 if(locked)
262 unlockdisplay(d);
263 break;
264 case TkTtab:
265 if(tb == nil)
266 w = 0;
267 else {
268 tbprev = nil;
269 while(tb->pos <= x && tb->next != nil) {
270 tbprev = tb;
271 tb = tb->next;
272 }
273 w = tb->pos - x;
274 if(w <= 0) {
275 del = tb->pos;
276 if(tbprev != nil)
277 del -= tbprev->pos;
278 while(w <= 0)
279 w += del;
280 }
281 /* todo: other kinds of justification */
282 }
283 break;
284 case TkTwin:
285 if(i->iwin->sub == 0)
286 w = 0;
287 else
288 w = i->iwin->sub->act.width + 2*i->iwin->padx + 2*i->iwin->sub->borderwidth;
289 break;
290 default:
291 w = 0;
292 }
293 return w;
294 }
295
296 int
tktindrune(TkTindex * ix)297 tktindrune(TkTindex *ix)
298 {
299 int ans;
300 Rune r;
301
302 switch(ix->item->kind) {
303 case TkTascii:
304 ans = ix->item->istring[ix->pos];
305 break;
306 case TkTrune:
307 chartorune(&r, ix->item->istring + tktutfpos(ix->item->istring, ix->pos));
308 ans = r;
309 break;
310 case TkTtab:
311 ans = '\t';
312 break;
313 case TkTnewline:
314 ans = '\n';
315 break;
316 default:
317 /* only care that it isn't a word char */
318 ans = 0x80;
319 }
320 return ans;
321 }
322
323 TkTitem*
tktlastitem(TkTitem * i)324 tktlastitem(TkTitem *i)
325 {
326 while(i->next != nil)
327 i = i->next;
328 if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline))
329 print("text:tktlastitem botch\n");
330
331 return i;
332 }
333
334 TkTline*
tktitemline(TkTitem * i)335 tktitemline(TkTitem *i)
336 {
337 i = tktlastitem(i);
338 return i->iline;
339 }
340
341 int
tktlinenum(TkText * tkt,TkTindex * p)342 tktlinenum(TkText *tkt, TkTindex *p)
343 {
344 int n;
345 TkTline *l;
346
347 if(p->line->orig.y <= tkt->end.orig.y / 2) {
348 /* line seems closer to beginning */
349 n = 1;
350 for(l = tkt->start.next; l != p->line; l = l->next) {
351 if(tktdbg && l->next == nil) {
352 print("text: tktlinenum botch\n");
353 break;
354 }
355 if(l->flags & TkTlast)
356 n++;
357 }
358 }
359 else {
360 n = tkt->nlines;
361 for(l = tkt->end.prev; l != p->line; l = l->prev) {
362 if(tktdbg && l->prev == nil) {
363 print("text: tktlinenum botch\n");
364 break;
365 }
366 if(l->flags & TkTfirst)
367 n--;
368 }
369 }
370 return n;
371 }
372
373 int
tktlinepos(TkText * tkt,TkTindex * p)374 tktlinepos(TkText *tkt, TkTindex *p)
375 {
376 int n;
377 TkTindex ix;
378 TkTitem *i;
379
380 n = 0;
381 ix = *p;
382 i = ix.item;
383 tktadjustind(tkt, TkTbylinestart, &ix);
384 while(ix.item != i) {
385 if(tktdbg && ix.item->next == nil && (ix.line->flags&TkTlast)) {
386 print("text: tktlinepos botch\n");
387 break;
388 }
389 n += tktposcount(ix.item);
390 if(!tktadjustind(tkt, TkTbyitem, &ix)) {
391 if(tktdbg)
392 print("tktlinepos botch\n");
393 break;
394 }
395 }
396 return (n+p->pos);
397 }
398
399 int
tktposcount(TkTitem * i)400 tktposcount(TkTitem *i)
401 {
402 int n;
403
404 if(i->kind == TkTascii)
405 n = strlen(i->istring);
406 else
407 if(i->kind == TkTrune)
408 n = utflen(i->istring);
409 else
410 if(i->kind == TkTmark || i->kind == TkTcontline)
411 n = 0;
412 else
413 n = 1;
414 return n;
415 }
416
417 /*
418 * Insert item i before position ins.
419 * If i is a newline or a contline, make a new line to contain the items up to
420 * and including the new newline, and make the original line
421 * contain the items from ins on.
422 * Adjust ins so that it points just after inserted item.
423 */
424 char*
tktiteminsert(TkText * tkt,TkTindex * ins,TkTitem * i)425 tktiteminsert(TkText *tkt, TkTindex *ins, TkTitem *i)
426 {
427 int hasprev, flags;
428 char *e;
429 TkTindex prev;
430 TkTline *l;
431 TkTitem *items;
432
433 prev = *ins;
434 hasprev = tktadjustind(tkt, TkTbyitemback, &prev);
435
436 if(i->kind == TkTnewline || i->kind == TkTcontline) {
437 i->next = nil;
438 if(hasprev && prev.line == ins->line) {
439 items = ins->line->items;
440 prev.item->next = i;
441 }
442 else
443 items = i;
444
445 flags = ins->line->flags&TkTfirst;
446 if(i->kind == TkTnewline)
447 flags |= TkTlast;
448 e = tktnewline(flags, items, ins->line->prev, ins->line, &l);
449 if(e != nil) {
450 if(hasprev && prev.line == ins->line)
451 prev.item->next = ins->item;
452 return e;
453 }
454
455 if(i->kind == TkTnewline)
456 ins->line->flags |= TkTfirst;
457
458 if(i->kind == TkTcontline)
459 ins->line->flags &= ~TkTfirst;
460 ins->line->items = ins->item;
461 ins->pos = 0;
462 }
463 else {
464 if(hasprev && prev.line == ins->line)
465 prev.item->next = i;
466 else
467 ins->line->items = i;
468 i->next = ins->item;
469 }
470
471 return nil;
472 }
473
474 /*
475 * If index p doesn't point at the beginning of an item,
476 * split the item at p. Adjust p to point to the beginning of
477 * the item after the split (same character it used to point at).
478 * If there is a split, the old item gets the characters before
479 * the split, and a new item gets the characters after it.
480 */
481 char*
tktsplititem(TkTindex * p)482 tktsplititem(TkTindex *p)
483 {
484 int l1, l2;
485 char *s1, *s2, *e;
486 TkTitem *i, *i2;
487
488 i = p->item;
489
490 if(p->pos != 0) {
491 /*
492 * Must be TkTascii or TkTrune
493 *
494 * Make new item i2, to be inserted after i,
495 * with portion of string from p->pos on
496 */
497
498 if (i->kind == TkTascii)
499 l1 = p->pos;
500 else
501 l1 = tktutfpos(i->istring, p->pos);
502 l2 = strlen(i->istring) - l1;
503 if (l2 == 0)
504 print("tktsplititem botch\n");
505 s1 = malloc(l1+1);
506 if(s1 == nil)
507 return TkNomem;
508 s2 = malloc(l2+1);
509 if(s2 == nil) {
510 free(s1);
511 return TkNomem;
512 }
513
514 memmove(s1, i->istring, l1);
515 s1[l1] = '\0';
516 memmove(s2, i->istring + l1, l2);
517 s2[l2] = '\0';
518
519 e = tktnewitem(i->kind, i->tagextra, &i2);
520 if(e != nil) {
521 free(s1);
522 free(s2);
523 return e;
524 }
525
526 free(i->istring);
527
528 tkttagcomb(i2, i, 1);
529 i2->next = i->next;
530 i->next = i2;
531 i->istring = s1;
532 i2->istring = s2;
533
534 p->item = i2;
535 p->pos = 0;
536 }
537
538 return nil;
539 }
540
541 int
tktmaxwid(TkTline * l)542 tktmaxwid(TkTline *l)
543 {
544 int w, maxw;
545
546 maxw = 0;
547 while(l != nil) {
548 w = l->width;
549 if(w > maxw)
550 maxw = w;
551 l = l->next;
552 }
553 return maxw;
554 }
555
556 Rectangle
tktbbox(Tk * tk,TkTindex * ix)557 tktbbox(Tk *tk, TkTindex *ix)
558 {
559 Rectangle r;
560 int d, w;
561 TkTitem *i;
562 TkTline *l;
563 TkEnv env;
564 TkTtabstop *tb = nil;
565 Tk *sub;
566 TkText *tkt = TKobj(TkText, tk);
567 int opts[TkTnumopts];
568
569 l = ix->line;
570
571 /* r in V space */
572 r.min = subpt(l->orig, tkt->deltatv);
573 r.min.y += l->ascent;
574 r.max = r.min;
575
576 /* tabs dependon tags of first non-mark on display line */
577 for(i = l->items; i->kind == TkTmark; )
578 i = i->next;
579 tkttagopts(tk, i, opts, &env, &tb, 1);
580
581 for(i = l->items; i != nil; i = i->next) {
582 if(i == ix->item) {
583 tkttagopts(tk, i, opts, &env, nil, 1);
584 r.min.y -= opts[TkToffset];
585 switch(i->kind) {
586 case TkTascii:
587 case TkTrune:
588 d = tktdispwidth(tk, tb, i, nil, r.min.x, 0, ix->pos);
589 w = tktdispwidth(tk, tb, i, nil, r.min.x, ix->pos, 1);
590 r.min.x += d;
591 r.min.y -= env.font->ascent;
592 r.max.x = r.min.x + w;
593 r.max.y = r.min.y + env.font->height;
594 break;
595 case TkTwin:
596 sub = i->iwin->sub;
597 if(sub == nil)
598 break;
599 r.min.x += sub->act.x;
600 r.min.y += sub->act.y;
601 r.max.x = r.min.x + sub->act.width + 2*sub->borderwidth;
602 r.max.y = r.min.y + sub->act.height + 2*sub->borderwidth;
603 break;
604 case TkTnewline:
605 r.max.x = r.min.x;
606 r.min.y -= l->ascent;
607 r.max.y = r.min.y + l->height;
608 break;
609 default:
610 d = tktdispwidth(tk, tb, i, nil, r.min.x, 0, -1);
611 r.max.x = r.min.x + d;
612 r.max.y = r.min.y;
613 break;
614 }
615 return r;
616 }
617 r.min.x += tktdispwidth(tk, tb, i, nil, r.min.x, 0, -1);
618 }
619 r.min.x = 0;
620 r.min.y = 0;
621 r.max.x = 0;
622 r.max.y = 0;
623 return r;
624 }
625
626 /* Return left-at-baseline position of given item, in V coords */
627 static Point
tktitempos(Tk * tk,TkTindex * ix)628 tktitempos(Tk *tk, TkTindex *ix)
629 {
630 Point p;
631 TkTitem *i;
632 TkTline *l;
633 TkText *tkt = TKobj(TkText, tk);
634
635 l = ix->line;
636
637 /* p in V space */
638 p = subpt(l->orig, tkt->deltatv);
639 p.y += l->ascent;
640
641 for(i = l->items; i != nil && i != ix->item; i = i->next)
642 p.x += i->width;
643 return p;
644 }
645
646 static Tk*
tktdeliver(Tk * tk,TkTitem * i,TkTitem * tagit,int event,void * data,Point deltasv)647 tktdeliver(Tk *tk, TkTitem *i, TkTitem *tagit, int event, void *data, Point deltasv)
648 {
649 Tk *ftk, *dest;
650 TkTwind *w;
651 TkText *tkt;
652 TkTtaginfo *t;
653 TkTline *l;
654 TkMouse m;
655 Point mp, p;
656 TkTindex ix;
657 int bd;
658
659 dest = nil;
660 if(i != nil) {
661 tkt = TKobj(TkText, tk);
662
663 if(i->kind == TkTwin) {
664 w = i->iwin;
665 if(w->sub != nil) {
666 if(!(event & TkKey) && (event & TkEmouse)) {
667 m = *(TkMouse*)data;
668 mp.x = m.x;
669 mp.y = m.y;
670 ix.item = i;
671 ix.pos = 0;
672 ix.line = tktitemline(i);
673 p = tktitempos(tk, &ix);
674 bd = w->sub->borderwidth;
675 mp.x = m.x - (deltasv.x + p.x + w->sub->act.x + bd);
676 mp.y = m.y - (deltasv.y + p.y + w->sub->act.y + bd);
677 ftk = tkinwindow(w->sub, mp, 0);
678 if(ftk != w->focus) {
679 tkdeliver(w->focus, TkLeave, data);
680 tkdeliver(ftk, TkEnter, data);
681
682 w->focus = ftk;
683 }
684 if(ftk != nil)
685 dest = tkdeliver(ftk, event, &m);
686 }
687 else {
688 if ((event & TkLeave) && (w->focus != w->sub)) {
689 tkdeliver(w->focus, TkLeave, data);
690 w->focus = nil;
691 event &= ~TkLeave;
692 }
693 if (event)
694 tkdeliver(w->sub, event, data);
695 }
696 if(Dx(w->sub->dirty) > 0) {
697 l = tktitemline(i);
698 tktfixgeom(tk, tktprevwrapline(tk, l), l, 0);
699 }
700 if(event & TkKey)
701 return dest;
702 }
703 }
704
705 if(tagit != 0) {
706 for(t = tkt->tags; t != nil; t = t->next) {
707 if(t->binds != nil && tkttagset(tagit, t->id)) {
708 if(tksubdeliver(tk, t->binds, event, data, 0) == TkDbreak) {
709 return dest;
710 }
711 }
712 }
713 }
714 }
715 return dest;
716 }
717
718 Tk*
tktinwindow(Tk * tk,Point * p)719 tktinwindow(Tk *tk, Point *p)
720 {
721 TkTindex ix;
722 Point q;
723 Tk *sub;
724
725 tktxyind(tk, p->x, p->y, &ix);
726 if (ix.item == nil || ix.item->kind != TkTwin || ix.item->iwin->sub == nil)
727 return tk;
728 sub = ix.item->iwin->sub;
729 q = tktitempos(tk, &ix);
730 p->x -= q.x + sub->borderwidth + sub->act.x;
731 p->y -= q.y + sub->borderwidth + sub->act.y;
732 return sub;
733 }
734
735 Tk*
tktextevent(Tk * tk,int event,void * data)736 tktextevent(Tk *tk, int event, void *data)
737 {
738 char *e;
739 TkMouse m, vm;
740 TkTitem *f, *tagit;
741 TkText *tkt;
742 TkTindex ix;
743 Tk *dest;
744 Point deltasv;
745
746 tkt = TKobj(TkText, tk);
747 deltasv = tkposn(tk);
748 deltasv.x += tk->borderwidth + tk->ipad.x/2;
749 deltasv.y += tk->borderwidth + tk->ipad.y/2;
750
751 dest = nil;
752 if(event == TkLeave && tkt->mouse != nil) {
753 vm.x = 0;
754 vm.y = 0;
755 tktdeliver(tk, tkt->mouse, tkt->mouse, TkLeave, data, deltasv);
756 tkt->mouse = nil;
757 }
758 else if((event & TkKey) == 0 && (event & TkEmouse)) {
759 /* m in S space, tm in V space */
760 m = *(TkMouse*)data;
761 vm = m;
762 vm.x -= deltasv.x;
763 vm.y -= deltasv.y;
764 if((event & TkMotion) == 0 || m.b == 0) {
765 tkt->current.x = vm.x;
766 tkt->current.y = vm.y;
767 }
768 tktxyind(tk, vm.x, vm.y, &ix);
769 f = ix.item;
770 if(tkt->mouse != f) {
771 tagit = nil;
772 if(tkt->mouse != nil) {
773 if(tktanytags(tkt->mouse)) {
774 e = tktnewitem(TkTascii, tkt->mouse->tagextra, &tagit);
775 if(e != nil)
776 return dest; /* XXX propagate error? */
777 tkttagcomb(tagit, tkt->mouse, 1);
778 tkttagcomb(tagit, f, -1);
779 }
780 tktdeliver(tk, tkt->mouse, tagit, TkLeave, data, deltasv);
781 if(tagit)
782 free(tagit);
783 tagit = nil;
784 }
785 if(tktanytags(f)) {
786 e = tktnewitem(TkTascii, f->tagextra, &tagit);
787 if(e != nil)
788 return dest; /* XXX propagate error? */
789 tkttagcomb(tagit, f, 1);
790 if(tkt->mouse)
791 tkttagcomb(tagit, tkt->mouse, -1);
792 }
793 tktdeliver(tk, f, tagit, TkEnter, data, deltasv);
794 tkt->mouse = f;
795 if(tagit)
796 free(tagit);
797 }
798 if(tkt->mouse != nil)
799 dest = tktdeliver(tk, tkt->mouse, tkt->mouse, event, &m, deltasv);
800 }
801 else if(event == TkFocusin)
802 tktextcursor(tk, " insert", (char **) nil);
803 /* pass all "real" events on to parent text widget - DBK */
804 tksubdeliver(tk, tk->binds, event, data, 0);
805 return dest;
806 }
807
808 /* Debugging */
809 void
tktprintitem(TkTitem * i)810 tktprintitem(TkTitem *i)
811 {
812 int j;
813
814 print("%p:", i);
815 switch(i->kind){
816 case TkTascii:
817 print("\"%s\"", i->istring);
818 break;
819 case TkTrune:
820 print("<rune:%s>", i->istring);
821 break;
822 case TkTnewline:
823 print("<nl:%p>", i->iline);
824 break;
825 case TkTcontline:
826 print("<cont:%p>", i->iline);
827 break;
828 case TkTtab:
829 print("<tab>");
830 break;
831 case TkTmark:
832 print("<mk:%s>", i->imark->name);
833 break;
834 case TkTwin:
835 if (i->iwin->sub->name != nil)
836 print("<win:%s>", i->iwin->sub? i->iwin->sub->name->name : "<null>");
837 }
838 print("[%d]", i->width);
839 if(i->tags !=0 || i->tagextra !=0) {
840 print("{%lux", i->tags[0]);
841 for(j=0; j < i->tagextra; j++)
842 print(" %lux", i->tags[j+1]);
843 print("}");
844 }
845 print(" ");
846 }
847
848 void
tktprintline(TkTline * l)849 tktprintline(TkTline *l)
850 {
851 TkTitem *i;
852
853 print("line %p: orig=(%d,%d), w=%d, h=%d, a=%d, f=%x\n\t",
854 l, l->orig.x, l->orig.y, l->width, l->height, l->ascent, l->flags);
855 for(i = l->items; i != nil; i = i->next)
856 tktprintitem(i);
857 print("\n");
858 }
859
860 void
tktprintindex(TkTindex * ix)861 tktprintindex(TkTindex *ix)
862 {
863 print("line=%p,item=%p,pos=%d\n", ix->line, ix->item, ix->pos);
864 }
865
866 void
tktprinttext(TkText * tkt)867 tktprinttext(TkText *tkt)
868 {
869 TkTline *l;
870 TkTtaginfo *ti;
871 TkTmarkinfo *mi;
872
873 for(ti=tkt->tags; ti != nil; ti=ti->next)
874 print("%s{%d} ", ti->name, ti->id);
875 print("\n");
876 for(mi = tkt->marks; mi != nil; mi=mi->next)
877 print("%s{%p} ", mi->name? mi->name : "nil", mi->cur);
878 print("\n");
879 print("selfirst=%p sellast=%p\n", tkt->selfirst, tkt->sellast);
880
881 for(l = &tkt->start; l != nil; l = l->next)
882 tktprintline(l);
883 }
884
885 /*
886 * Check that assumed invariants are true.
887 *
888 * - start line and end line have no items
889 * - all other lines have at least one item
890 * - start line leads to end line via next pointers
891 * - prev pointers point to previous lines
892 * - each line ends in either a TkTnewline or a TkTcontline
893 * whose iline pointer points to the line itself
894 * - TkTcontline and TkTmark items have no tags
895 * (this is so they don't get realloc'd because of tag combination)
896 * - all cur fields of marks point to nil or a TkTmark
897 * - selfirst and sellast correctly define select region
898 * - nlines counts the number of lines
899 */
900 void
tktcheck(TkText * tkt,char * fun)901 tktcheck(TkText *tkt, char *fun)
902 {
903 int nl, insel, selfound;
904 TkTline *l;
905 TkTitem *i;
906 TkTmarkinfo *mi;
907 TkTindex ix;
908 char *prob;
909
910 prob = nil;
911 nl = 0;
912
913 if(tkt->start.items != nil || tkt->end.items != nil)
914 prob = "start/end has items";
915 for(l = tkt->start.next; l != &tkt->end; l = l->next) {
916 if(l->prev->next != l) {
917 prob = "prev mismatch";
918 break;
919 }
920 if(l->next->prev != l) {
921 prob = "next mismatch";
922 break;
923 }
924 i = l->items;
925 if(i == nil) {
926 prob = "empty line";
927 break;
928 }
929 while(i->next != nil) {
930 if(i->kind == TkTnewline || i->kind == TkTcontline) {
931 prob = "premature end of line";
932 break;
933 }
934 if(i->kind == TkTmark && (i->tags[0] != 0 || i->tagextra != 0)) {
935 prob = "mark has tags";
936 break;
937 }
938 i = i->next;
939 }
940 if(i->kind == TkTnewline)
941 nl++;
942 if(!(i->kind == TkTnewline || i->kind == TkTcontline)) {
943 prob = "bad end of line";
944 break;
945 }
946 if(i->kind == TkTcontline && (i->tags[0] != 0 || i->tagextra != 0)) {
947 prob = "contline has tags";
948 break;
949 }
950 if(i->iline != l) {
951 prob = "bad end-of-line pointer";
952 break;
953 }
954 }
955 for(mi = tkt->marks; mi != nil; mi=mi->next) {
956 if(mi->cur != nil) {
957 tktstartind(tkt, &ix);
958 do {
959 if(ix.item->kind == TkTmark && ix.item == mi->cur)
960 goto foundmark;
961 } while(tktadjustind(tkt, TkTbyitem, &ix));
962 prob = "bad mark cur";
963 break;
964 foundmark: ;
965 }
966 }
967 insel = 0;
968 selfound = 0;
969 tktstartind(tkt, &ix);
970 do {
971 i = ix.item;
972 if(i == tkt->selfirst) {
973 if(i->kind == TkTmark || i->kind == TkTcontline) {
974 prob = "selfirst not on character";
975 break;
976 }
977 if(i == tkt->sellast) {
978 prob = "selfirst==sellast, not nil";
979 break;
980 }
981 insel = 1;
982 selfound = 1;
983 }
984 if(i == tkt->sellast) {
985 if(i->kind == TkTmark || i->kind == TkTcontline) {
986 prob = "sellast not on character";
987 break;
988 }
989 insel = 0;
990 }
991 if(i->kind != TkTmark && i->kind != TkTcontline) {
992 if(i->tags[0] & (1<<TkTselid)) {
993 if(!insel) {
994 prob = "sel set outside selfirst..sellast";
995 break;
996 }
997 }
998 else {
999 if(insel) {
1000 prob = "sel not set inside selfirst..sellast";
1001 break;
1002 }
1003 }
1004 }
1005 } while(tktadjustind(tkt, TkTbyitem, &ix));
1006 if(tkt->selfirst != nil && !selfound)
1007 prob = "selfirst not found";
1008
1009 if(prob != nil) {
1010 print("tktcheck problem: %s: %s\n", fun, prob);
1011 tktprinttext(tkt);
1012 abort();
1013 }
1014 }
1015
1016 int
tktutfpos(char * s,int pos)1017 tktutfpos(char *s, int pos)
1018 {
1019 char *s1;
1020 int c;
1021 Rune r;
1022
1023 for (s1 = s; pos > 0; pos--) {
1024 c = *(uchar *)s1;
1025 if (c < Runeself) {
1026 if (c == '\0')
1027 break;
1028 s1++;
1029 }
1030 else
1031 s1 += chartorune(&r, s1);
1032 }
1033 return s1 - s;
1034 }
1035
1036 /*
1037 struct timerec {
1038 char *name;
1039 ulong ms;
1040 };
1041
1042 static struct timerec tt[100];
1043 static int ntt = 1;
1044
1045 int
1046 tktalloctime(char *name)
1047 {
1048 if(ntt >= 100)
1049 abort();
1050 tt[ntt].name = strdup(name);
1051 tt[ntt].ms = 0;
1052 return ntt++;
1053 }
1054
1055 void
1056 tktstarttime(int ind)
1057 {
1058 return;
1059 tt[ind].ms -= osmillisec();
1060 }
1061
1062 void
1063 tktendtime(int ind)
1064 {
1065 return;
1066 tt[ind].ms += osmillisec();
1067 }
1068
1069 void
1070 tktdumptime(void)
1071 {
1072 int i;
1073
1074 for(i = 1; i < ntt; i++)
1075 print("%s: %d\n", tt[i].name, tt[i].ms);
1076 }
1077 */
1078