xref: /inferno-os/libtk/textu.c (revision fd7058f9a883832e948d667b63c56178e37b1e15)
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*
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*
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
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
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
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
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
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
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
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*
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*
335 tktitemline(TkTitem *i)
336 {
337 	i = tktlastitem(i);
338 	return i->iline;
339 }
340 
341 int
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
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
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*
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*
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
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
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
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*
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*
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*
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
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
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
861 tktprintindex(TkTindex *ix)
862 {
863 	print("line=%p,item=%p,pos=%d\n", ix->line, ix->item, ix->pos);
864 }
865 
866 void
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
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
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