xref: /inferno-os/libtk/textu.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
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 
575 	/* tabs dependon tags of first non-mark on display line */
576 	for(i = l->items; i->kind == TkTmark; )
577 		i = i->next;
578 	tkttagopts(tk, i, opts, &env, &tb, 1);
579 
580 	for(i = l->items; i != nil; i = i->next) {
581 		if(i == ix->item) {
582 			tkttagopts(tk, i, opts, &env, nil, 1);
583 			r.min.y -= opts[TkToffset];
584 			switch(i->kind) {
585 			case TkTascii:
586 			case TkTrune:
587 				d = tktdispwidth(tk, tb, i, nil, r.min.x, 0, ix->pos);
588 				w = tktdispwidth(tk, tb, i, nil, r.min.x, ix->pos, 1);
589 				r.min.x += d;
590 				r.min.y -= env.font->ascent;
591 				r.max.x = r.min.x + w;
592 				r.max.y = r.min.y + env.font->height;
593 				break;
594 			case TkTwin:
595 				sub = i->iwin->sub;
596 				if(sub == nil)
597 					break;
598 				r.min.x += sub->act.x;
599 				r.min.y += sub->act.y;
600 				r.max.x = r.min.x + sub->act.width + 2*sub->borderwidth;
601 				r.max.y = r.min.y + sub->act.height + 2*sub->borderwidth;
602 				break;
603 			case TkTnewline:
604 				r.max.x = r.min.x;
605 				r.min.y -= l->ascent;
606 				r.max.y = r.min.y + l->height;
607 				break;
608 			default:
609 				d = tktdispwidth(tk, tb, i, nil, r.min.x, 0, -1);
610 				r.max.x = r.min.x + d;
611 				r.max.y = r.min.y;
612 				break;
613 			}
614 			return r;
615 		}
616 		r.min.x += tktdispwidth(tk, tb, i, nil, r.min.x, 0, -1);
617 	}
618 	r.min.x = 0;
619 	r.min.y = 0;
620 	r.max.x = 0;
621 	r.max.y = 0;
622 	return r;
623 }
624 
625 /* Return left-at-baseline position of given item, in V coords */
626 static Point
627 tktitempos(Tk *tk, TkTindex *ix)
628 {
629 	Point p;
630 	TkTitem *i;
631 	TkTline *l;
632 	TkText *tkt = TKobj(TkText, tk);
633 
634 	l = ix->line;
635 
636 	/* p in V space */
637 	p = subpt(l->orig, tkt->deltatv);
638 	p.y += l->ascent;
639 
640 	for(i = l->items; i != nil && i != ix->item; i = i->next)
641 		p.x += i->width;
642 	return p;
643 }
644 
645 static Tk*
646 tktdeliver(Tk *tk, TkTitem *i, TkTitem *tagit, int event, void *data, Point deltasv)
647 {
648 	Tk *ftk, *dest;
649 	TkTwind *w;
650 	TkText *tkt;
651 	TkTtaginfo *t;
652 	TkTline *l;
653 	TkMouse m;
654 	Point mp, p;
655 	TkTindex ix;
656 	int bd;
657 
658 	dest = nil;
659 	if(i != nil) {
660 		tkt = TKobj(TkText, tk);
661 
662 		if(i->kind == TkTwin) {
663 			w = i->iwin;
664 			if(w->sub != nil) {
665 				if(!(event & TkKey) && (event & TkEmouse)) {
666 					m = *(TkMouse*)data;
667 					mp.x = m.x;
668 					mp.y = m.y;
669 					ix.item = i;
670 					ix.pos = 0;
671 					ix.line = tktitemline(i);
672 					p = tktitempos(tk, &ix);
673 					bd = w->sub->borderwidth;
674 					mp.x = m.x - (deltasv.x + p.x + w->sub->act.x + bd);
675 					mp.y = m.y - (deltasv.y + p.y + w->sub->act.y + bd);
676 					ftk = tkinwindow(w->sub, mp, 0);
677 					if(ftk != w->focus) {
678 						tkdeliver(w->focus, TkLeave, data);
679 						tkdeliver(ftk, TkEnter, data);
680 
681 						w->focus = ftk;
682 					}
683 					if(ftk != nil)
684 						dest = tkdeliver(ftk, event, &m);
685 				}
686 				else {
687 					if ((event & TkLeave) && (w->focus != w->sub)) {
688 						tkdeliver(w->focus, TkLeave, data);
689 						w->focus = nil;
690 						event &= ~TkLeave;
691 					}
692 					if (event)
693 						tkdeliver(w->sub, event, data);
694 				}
695 				if(Dx(w->sub->dirty) > 0) {
696 					l = tktitemline(i);
697 					tktfixgeom(tk, tktprevwrapline(tk, l), l, 0);
698 				}
699 				if(event & TkKey)
700 					return dest;
701 			}
702 		}
703 
704 		if(tagit != 0) {
705 			for(t = tkt->tags; t != nil; t = t->next) {
706 				if(t->binds != nil && tkttagset(tagit, t->id)) {
707 					if(tksubdeliver(tk, t->binds, event, data, 0) == TkDbreak) {
708 						return dest;
709 					}
710 				}
711 			}
712 		}
713 	}
714 	return dest;
715 }
716 
717 Tk*
718 tktinwindow(Tk *tk, Point *p)
719 {
720 	TkTindex ix;
721 	Point q;
722 	Tk *sub;
723 
724 	tktxyind(tk, p->x, p->y, &ix);
725 	if (ix.item == nil || ix.item->kind != TkTwin || ix.item->iwin->sub == nil)
726 		return tk;
727 	sub = ix.item->iwin->sub;
728 	q = tktitempos(tk, &ix);
729 	p->x -= q.x + sub->borderwidth + sub->act.x;
730 	p->y -= q.y + sub->borderwidth + sub->act.y;
731 	return sub;
732 }
733 
734 Tk*
735 tktextevent(Tk *tk, int event, void *data)
736 {
737 	char *e;
738 	TkMouse m, vm;
739 	TkTitem *f, *tagit;
740 	TkText *tkt;
741 	TkTindex ix;
742 	Tk *dest;
743 	Point deltasv;
744 
745 	tkt = TKobj(TkText, tk);
746 	deltasv = tkposn(tk);
747 	deltasv.x += tk->borderwidth + tk->ipad.x/2;
748 	deltasv.y += tk->borderwidth + tk->ipad.y/2;
749 
750 	dest = nil;
751 	if(event == TkLeave && tkt->mouse != nil) {
752 		vm.x = 0;
753 		vm.y = 0;
754 		tktdeliver(tk, tkt->mouse, tkt->mouse, TkLeave, data, deltasv);
755 		tkt->mouse = nil;
756 	}
757 	else if((event & TkKey) == 0 && (event & TkEmouse)) {
758 		/* m in S space, tm in V space */
759 		m = *(TkMouse*)data;
760 		vm = m;
761 		vm.x -= deltasv.x;
762 		vm.y -= deltasv.y;
763 		if((event & TkMotion) == 0 || m.b == 0) {
764 			tkt->current.x = vm.x;
765 			tkt->current.y = vm.y;
766 		}
767 		tktxyind(tk, vm.x, vm.y, &ix);
768 		f = ix.item;
769 		if(tkt->mouse != f) {
770 			tagit = nil;
771 			if(tkt->mouse != nil) {
772 				if(tktanytags(tkt->mouse)) {
773 					e = tktnewitem(TkTascii, tkt->mouse->tagextra, &tagit);
774 					if(e != nil)
775 						return dest;	/* XXX propagate error? */
776 					tkttagcomb(tagit, tkt->mouse, 1);
777 					tkttagcomb(tagit, f, -1);
778 				}
779 				tktdeliver(tk, tkt->mouse, tagit, TkLeave, data, deltasv);
780 				if(tagit)
781 					free(tagit);
782 				tagit = nil;
783 			}
784 			if(tktanytags(f)) {
785 				e = tktnewitem(TkTascii, f->tagextra, &tagit);
786 				if(e != nil)
787 					return dest;		/* XXX propagate error? */
788 				tkttagcomb(tagit, f, 1);
789 				if(tkt->mouse)
790 					tkttagcomb(tagit, tkt->mouse, -1);
791 			}
792 			tktdeliver(tk, f, tagit, TkEnter, data, deltasv);
793 			tkt->mouse = f;
794 			if(tagit)
795 				free(tagit);
796 		}
797 		if(tkt->mouse != nil)
798 			dest = tktdeliver(tk, tkt->mouse, tkt->mouse, event, &m, deltasv);
799 	}
800 	else if(event == TkFocusin)
801 		tktextcursor(tk, " insert", (char **) nil);
802 	/* pass all "real" events on to parent text widget - DBK */
803 	tksubdeliver(tk, tk->binds, event, data, 0);
804 	return dest;
805 }
806 
807 /* Debugging */
808 void
809 tktprintitem(TkTitem *i)
810 {
811 	int j;
812 
813 	print("%p:", i);
814 	switch(i->kind){
815 	case TkTascii:
816 		print("\"%s\"", i->istring);
817 		break;
818 	case TkTrune:
819 		print("<rune:%s>", i->istring);
820 		break;
821 	case TkTnewline:
822 		print("<nl:%p>", i->iline);
823 		break;
824 	case TkTcontline:
825 		print("<cont:%p>", i->iline);
826 		break;
827 	case TkTtab:
828 		print("<tab>");
829 		break;
830 	case TkTmark:
831 		print("<mk:%s>", i->imark->name);
832 		break;
833 	case TkTwin:
834 	        if (i->iwin->sub->name != nil)
835 		  print("<win:%s>", i->iwin->sub? i->iwin->sub->name->name : "<null>");
836 	}
837 	print("[%d]", i->width);
838 	if(i->tags !=0 || i->tagextra !=0) {
839 		print("{%lux", i->tags[0]);
840 		for(j=0; j < i->tagextra; j++)
841 			print(" %lux", i->tags[j+1]);
842 		print("}");
843 	}
844 	print(" ");
845 }
846 
847 void
848 tktprintline(TkTline *l)
849 {
850 	TkTitem *i;
851 
852 	print("line %p: orig=(%d,%d), w=%d, h=%d, a=%d, f=%x\n\t",
853 		l, l->orig.x, l->orig.y, l->width, l->height, l->ascent, l->flags);
854 	for(i = l->items; i != nil; i = i->next)
855 		tktprintitem(i);
856 	print("\n");
857 }
858 
859 void
860 tktprintindex(TkTindex *ix)
861 {
862 	print("line=%p,item=%p,pos=%d\n", ix->line, ix->item, ix->pos);
863 }
864 
865 void
866 tktprinttext(TkText *tkt)
867 {
868 	TkTline *l;
869 	TkTtaginfo *ti;
870 	TkTmarkinfo *mi;
871 
872 	for(ti=tkt->tags; ti != nil; ti=ti->next)
873 		print("%s{%d} ", ti->name, ti->id);
874 	print("\n");
875 	for(mi = tkt->marks; mi != nil; mi=mi->next)
876 		print("%s{%p} ", mi->name? mi->name : "nil", mi->cur);
877 	print("\n");
878 	print("selfirst=%p sellast=%p\n", tkt->selfirst, tkt->sellast);
879 
880 	for(l = &tkt->start; l != nil; l = l->next)
881 		tktprintline(l);
882 }
883 
884 /*
885  * Check that assumed invariants are true.
886  *
887  * - start line and end line have no items
888  * - all other lines have at least one item
889  * - start line leads to end line via next pointers
890  * - prev pointers point to previous lines
891  * - each line ends in either a TkTnewline or a TkTcontline
892  *    whose iline pointer points to the line itself
893  * - TkTcontline and TkTmark items have no tags
894  *    (this is so they don't get realloc'd because of tag combination)
895  * - all cur fields of marks point to nil or a TkTmark
896  * - selfirst and sellast correctly define select region
897  * - nlines counts the number of lines
898  */
899 void
900 tktcheck(TkText *tkt, char *fun)
901 {
902 	int nl, insel, selfound;
903 	TkTline *l;
904 	TkTitem *i;
905 	TkTmarkinfo *mi;
906 	TkTindex ix;
907 	char *prob;
908 
909 	prob = nil;
910 	nl = 0;
911 
912 	if(tkt->start.items != nil || tkt->end.items != nil)
913 		prob = "start/end has items";
914 	for(l = tkt->start.next; l != &tkt->end; l = l->next) {
915 		if(l->prev->next != l) {
916 			prob = "prev mismatch";
917 			break;
918 		}
919 		if(l->next->prev != l) {
920 			prob = "next mismatch";
921 			break;
922 		}
923 		i = l->items;
924 		if(i == nil) {
925 			prob = "empty line";
926 			break;
927 		}
928 		while(i->next != nil) {
929 			if(i->kind == TkTnewline || i->kind == TkTcontline) {
930 				prob = "premature end of line";
931 				break;
932 			}
933 			if(i->kind == TkTmark && (i->tags[0] != 0 || i->tagextra != 0)) {
934 				prob = "mark has tags";
935 				break;
936 			}
937 			i = i->next;
938 		}
939 		if(i->kind == TkTnewline)
940 			nl++;
941 		if(!(i->kind == TkTnewline || i->kind == TkTcontline)) {
942 			prob = "bad end of line";
943 			break;
944 		}
945 		if(i->kind == TkTcontline && (i->tags[0] != 0 || i->tagextra != 0)) {
946 			prob = "contline has tags";
947 			break;
948 		}
949 		if(i->iline != l) {
950 			prob = "bad end-of-line pointer";
951 			break;
952 		}
953 	}
954 	for(mi = tkt->marks; mi != nil; mi=mi->next) {
955 		if(mi->cur != nil) {
956 			tktstartind(tkt, &ix);
957 			do {
958 				if(ix.item->kind == TkTmark && ix.item == mi->cur)
959 					goto foundmark;
960 			} while(tktadjustind(tkt, TkTbyitem, &ix));
961 			prob = "bad mark cur";
962 			break;
963 		    foundmark: ;
964 		}
965 	}
966 	insel = 0;
967 	selfound = 0;
968 	tktstartind(tkt, &ix);
969 	do {
970 		i = ix.item;
971 		if(i == tkt->selfirst) {
972 			if(i->kind == TkTmark || i->kind == TkTcontline) {
973 				prob = "selfirst not on character";
974 				break;
975 			}
976 			if(i == tkt->sellast) {
977 				prob = "selfirst==sellast, not nil";
978 				break;
979 			}
980 			insel = 1;
981 			selfound = 1;
982 		}
983 		if(i == tkt->sellast) {
984 			if(i->kind == TkTmark || i->kind == TkTcontline) {
985 				prob = "sellast not on character";
986 				break;
987 			}
988 			insel = 0;
989 		}
990 		if(i->kind != TkTmark && i->kind != TkTcontline) {
991 			if(i->tags[0] & (1<<TkTselid)) {
992 				if(!insel) {
993 					prob = "sel set outside selfirst..sellast";
994 					break;
995 				}
996 			}
997 			else {
998 				if(insel) {
999 					prob = "sel not set inside selfirst..sellast";
1000 					break;
1001 				}
1002 			}
1003 		}
1004 	} while(tktadjustind(tkt, TkTbyitem, &ix));
1005 	if(tkt->selfirst != nil && !selfound)
1006 		prob = "selfirst not found";
1007 
1008 	if(prob != nil) {
1009 		print("tktcheck problem: %s: %s\n", fun, prob);
1010 		tktprinttext(tkt);
1011 abort();
1012 	}
1013 }
1014 
1015 int
1016 tktutfpos(char *s, int pos)
1017 {
1018 	char *s1;
1019 	int c;
1020 	Rune r;
1021 
1022 	for (s1 = s; pos > 0; pos--) {
1023 		c = *(uchar *)s1;
1024 		if (c < Runeself) {
1025 			if (c == '\0')
1026 				break;
1027 			s1++;
1028 		}
1029 		else
1030 			s1 += chartorune(&r, s1);
1031 	}
1032 	return s1 - s;
1033 }
1034 
1035 /*
1036 struct timerec {
1037 	char *name;
1038 	ulong ms;
1039 };
1040 
1041 static struct timerec tt[100];
1042 static int ntt = 1;
1043 
1044 int
1045 tktalloctime(char *name)
1046 {
1047 	if(ntt >= 100)
1048 		abort();
1049 	tt[ntt].name = strdup(name);
1050 	tt[ntt].ms = 0;
1051 	return ntt++;
1052 }
1053 
1054 void
1055 tktstarttime(int ind)
1056 {
1057 return;
1058 	tt[ind].ms -= osmillisec();
1059 }
1060 
1061 void
1062 tktendtime(int ind)
1063 {
1064 return;
1065 	tt[ind].ms += osmillisec();
1066 }
1067 
1068 void
1069 tktdumptime(void)
1070 {
1071 	int i;
1072 
1073 	for(i = 1; i < ntt; i++)
1074 		print("%s: %d\n", tt[i].name, tt[i].ms);
1075 }
1076 */
1077