xref: /inferno-os/libtk/textu.c (revision 6e425a9de8c003b5a733621a6b6730ec3cc902b8)
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