xref: /inferno-os/libtk/textw.c (revision 3a78c580bfacbdc12a08507f98b0dfb3c9b78f94)
137da2899SCharles.Forsyth #include "lib9.h"
237da2899SCharles.Forsyth #include "draw.h"
337da2899SCharles.Forsyth #include "keyboard.h"
437da2899SCharles.Forsyth #include "tk.h"
537da2899SCharles.Forsyth #include "textw.h"
637da2899SCharles.Forsyth 
737da2899SCharles.Forsyth /*
837da2899SCharles.Forsyth  * useful text widget info to be found at:
937da2899SCharles.Forsyth  * :/coordinate.systems		- what coord. systems are in use
1037da2899SCharles.Forsyth  * textu.c:/assumed.invariants	- some invariants that must be preserved
1137da2899SCharles.Forsyth  */
1237da2899SCharles.Forsyth 
1337da2899SCharles.Forsyth #define istring u.string
1437da2899SCharles.Forsyth #define iwin u.win
1537da2899SCharles.Forsyth #define imark u.mark
1637da2899SCharles.Forsyth #define iline u.line
1737da2899SCharles.Forsyth 
1837da2899SCharles.Forsyth #define FLUSH() flushimage(tk->env->top->display, 1)
1937da2899SCharles.Forsyth 
2037da2899SCharles.Forsyth #define	O(t, e)		((long)(&((t*)0)->e))
2137da2899SCharles.Forsyth 
2237da2899SCharles.Forsyth /* Layout constants */
2337da2899SCharles.Forsyth enum {
2437da2899SCharles.Forsyth 	Textpadx	= 2,
2537da2899SCharles.Forsyth 	Textpady	= 0,
2637da2899SCharles.Forsyth };
2737da2899SCharles.Forsyth 
2837da2899SCharles.Forsyth typedef struct Interval {
2937da2899SCharles.Forsyth 	int	lo;
3037da2899SCharles.Forsyth 	int	hi;
3137da2899SCharles.Forsyth } Interval;
3237da2899SCharles.Forsyth 
3337da2899SCharles.Forsyth typedef struct Mprint Mprint;
3437da2899SCharles.Forsyth struct Mprint
3537da2899SCharles.Forsyth {
3637da2899SCharles.Forsyth 	char*	buf;
3737da2899SCharles.Forsyth 	int	ptr;
3837da2899SCharles.Forsyth 	int	len;
3937da2899SCharles.Forsyth };
4037da2899SCharles.Forsyth 
4137da2899SCharles.Forsyth typedef struct TkDump TkDump;
4237da2899SCharles.Forsyth struct TkDump
4337da2899SCharles.Forsyth {
4437da2899SCharles.Forsyth 	int	sgml;
4537da2899SCharles.Forsyth 	int	metrics;
4637da2899SCharles.Forsyth };
4737da2899SCharles.Forsyth 
4837da2899SCharles.Forsyth static
4937da2899SCharles.Forsyth TkOption dumpopts[] =
5037da2899SCharles.Forsyth {
5137da2899SCharles.Forsyth 	"sgml",		OPTbool,	O(TkDump, sgml),	nil,
5237da2899SCharles.Forsyth 	"metrics",	OPTbool,	O(TkDump, metrics),	nil,
5337da2899SCharles.Forsyth 	nil
5437da2899SCharles.Forsyth };
5537da2899SCharles.Forsyth 
5637da2899SCharles.Forsyth static
5737da2899SCharles.Forsyth TkStab tkcompare[] =
5837da2899SCharles.Forsyth {
5937da2899SCharles.Forsyth 	"<",		TkLt,
6037da2899SCharles.Forsyth 	"<=",		TkLte,
6137da2899SCharles.Forsyth 	"==",		TkEq,
6237da2899SCharles.Forsyth 	">=",		TkGte,
6337da2899SCharles.Forsyth 	">",		TkGt,
6437da2899SCharles.Forsyth 	"!=",		TkNeq,
6537da2899SCharles.Forsyth 	nil
6637da2899SCharles.Forsyth };
6737da2899SCharles.Forsyth 
6837da2899SCharles.Forsyth static
6937da2899SCharles.Forsyth TkOption textopts[] =
7037da2899SCharles.Forsyth {
7137da2899SCharles.Forsyth 	"wrap",			OPTstab, O(TkText, opts[TkTwrap]),	tkwrap,
7237da2899SCharles.Forsyth 	"spacing1",		OPTnndist, O(TkText, opts[TkTspacing1]),	(void *)O(Tk, env),
7337da2899SCharles.Forsyth 	"spacing2",		OPTnndist, O(TkText, opts[TkTspacing2]),	(void *)O(Tk, env),
7437da2899SCharles.Forsyth 	"spacing3",		OPTnndist, O(TkText, opts[TkTspacing3]),	(void *)O(Tk, env),
7537da2899SCharles.Forsyth 	"tabs",			OPTtabs, O(TkText, tabs), 		(void *)O(Tk, env),
7637da2899SCharles.Forsyth 	"xscrollcommand",	OPTtext, O(TkText, xscroll),		nil,
7737da2899SCharles.Forsyth 	"yscrollcommand",	OPTtext, O(TkText, yscroll),		nil,
7837da2899SCharles.Forsyth 	"insertwidth",		OPTnndist, O(TkText, inswidth),		nil,
7937da2899SCharles.Forsyth 	"tagshare",		OPTwinp, O(TkText, tagshare),		nil,
8037da2899SCharles.Forsyth 	"propagate",		OPTstab, O(TkText, propagate),	tkbool,
8137da2899SCharles.Forsyth 	"selectborderwidth",	OPTnndist, O(TkText, sborderwidth), nil,
8237da2899SCharles.Forsyth 	nil
8337da2899SCharles.Forsyth };
8437da2899SCharles.Forsyth 
8537da2899SCharles.Forsyth #define CNTL(c) ((c)&0x1f)
8637da2899SCharles.Forsyth #define DEL 0x7f
8737da2899SCharles.Forsyth 
8837da2899SCharles.Forsyth static TkEbind tktbinds[] = {
8937da2899SCharles.Forsyth 	{TkButton1P,		"%W tkTextButton1 %X %Y"},
9037da2899SCharles.Forsyth 	{TkButton1P|TkMotion,	"%W tkTextSelectTo %X %Y"},
9137da2899SCharles.Forsyth 	{TkButton1P|TkDouble,	"%W tkTextSelectTo %X %Y double"},
9237da2899SCharles.Forsyth 	{TkButton1R,		"%W tkTextButton1R"},
9337da2899SCharles.Forsyth 	{TkButton2P,		"%W scan mark %x %y"},
9437da2899SCharles.Forsyth 	{TkButton2P|TkMotion,	"%W scan dragto %x %y"},
9537da2899SCharles.Forsyth 	{TkKey,			"%W tkTextInsert {%A}"},
9637da2899SCharles.Forsyth 	{TkKey|CNTL('a'),	"%W tkTextSetCursor {insert linestart}"},
9737da2899SCharles.Forsyth 	{TkKey|Home,		"%W tkTextSetCursor {insert linestart}"},
9837da2899SCharles.Forsyth 	{TkKey|CNTL('<'),	"%W tkTextSetCursor {insert linestart}"},
9937da2899SCharles.Forsyth 	{TkKey|CNTL('b'),	"%W tkTextSetCursor insert-1c"},
10037da2899SCharles.Forsyth 	{TkKey|Left,		"%W tkTextSetCursor insert-1c"},
101*3a78c580Sforsyth 	{TkKey|CNTL('d'),	"%W tkTextDelIns"},
10237da2899SCharles.Forsyth 	{TkKey|CNTL('e'),	"%W tkTextSetCursor {insert lineend}"},
10337da2899SCharles.Forsyth 	{TkKey|End,		"%W tkTextSetCursor {insert lineend}"},
10437da2899SCharles.Forsyth 	{TkKey|CNTL('>'),	"%W tkTextSetCursor {insert lineend}"},
10537da2899SCharles.Forsyth 	{TkKey|CNTL('f'),	"%W tkTextSetCursor insert+1c"},
10637da2899SCharles.Forsyth 	{TkKey|Right,		"%W tkTextSetCursor insert+1c"},
10737da2899SCharles.Forsyth 	{TkKey|CNTL('h'),	"%W tkTextDelIns -c"},
108*3a78c580Sforsyth 	{TkKey|DEL,		"%W tkTextDelIns"},
109*3a78c580Sforsyth 	{TkKey|CNTL('k'),	"%W tkTextDelIns +l"},
11037da2899SCharles.Forsyth 	{TkKey|CNTL('n'),	"%W tkTextSetCursor {insert+1l}"},
11137da2899SCharles.Forsyth 	{TkKey|Down,		"%W tkTextSetCursor {insert+1l}"},
11237da2899SCharles.Forsyth 	{TkKey|CNTL('o'),       "%W tkTextInsert {\n}; %W mark set insert insert-1c"},
11337da2899SCharles.Forsyth 	{TkKey|CNTL('p'),	"%W tkTextSetCursor {insert-1l}"},
11437da2899SCharles.Forsyth 	{TkKey|Up,		"%W tkTextSetCursor {insert-1l}"},
11537da2899SCharles.Forsyth 	{TkKey|CNTL('u'),	"%W tkTextDelIns -l"},
1166e425a9dSCharles.Forsyth 	{TkKey|CNTL('v'),	"%W yview scroll 0.75 page"},
1176e425a9dSCharles.Forsyth 	{TkKey|Pgdown,	"%W yview scroll 0.75 page"},
11837da2899SCharles.Forsyth 	{TkKey|CNTL('w'),	"%W tkTextDelIns -w"},
1196e425a9dSCharles.Forsyth 	{TkKey|Pgup,	"%W yview scroll -0.75 page"},
1205849851aSforsyth 	{TkButton4P,	"%W yview scroll -0.2 page"},
1215849851aSforsyth 	{TkButton5P,	"%W yview scroll 0.2 page"},
12237da2899SCharles.Forsyth 	{TkFocusout,            "%W tkTextCursor delete"},
12337da2899SCharles.Forsyth 	{TkKey|APP|'\t',	""},
12437da2899SCharles.Forsyth 	{TkKey|BackTab,		""},
12537da2899SCharles.Forsyth };
12637da2899SCharles.Forsyth 
12737da2899SCharles.Forsyth static int	tktclickmatch(TkText *, int, int, int, TkTindex *);
12837da2899SCharles.Forsyth static void	tktdoubleclick(TkText *, TkTindex *, TkTindex *);
12937da2899SCharles.Forsyth static char* 	tktdrawline(Image*, Tk*, TkTline*, Point);
13037da2899SCharles.Forsyth static void	tktextcursordraw(Tk *, int);
13137da2899SCharles.Forsyth static char* 	tktsetscroll(Tk*, int);
13237da2899SCharles.Forsyth static void	tktsetclip(Tk *);
13337da2899SCharles.Forsyth static char* 	tktview(Tk*, char*, char**, int, int*, int, int);
13437da2899SCharles.Forsyth static Interval tkttranslate(Tk*, Interval, int);
13537da2899SCharles.Forsyth static void 	tktfixscroll(Tk*, Point);
13637da2899SCharles.Forsyth static void 	tktnotdrawn(Tk*, int, int, int);
13737da2899SCharles.Forsyth static void	tktdrawbg(Tk*, int, int, int);
13837da2899SCharles.Forsyth static int	tktwidbetween(Tk*, int, TkTindex*, TkTindex*);
13937da2899SCharles.Forsyth static int	tktpostspace(Tk*, TkTline*);
14037da2899SCharles.Forsyth static int	tktprespace(Tk*, TkTline*);
14137da2899SCharles.Forsyth static void	tktsee(Tk*, TkTindex*, int);
14237da2899SCharles.Forsyth static Point	tktrelpos(Tk*);
14337da2899SCharles.Forsyth static void	autoselect(Tk*, void*, int);
14437da2899SCharles.Forsyth static void	blinkreset(Tk*);
14537da2899SCharles.Forsyth 
14637da2899SCharles.Forsyth /* debugging */
14737da2899SCharles.Forsyth extern int tktdbg;
14837da2899SCharles.Forsyth extern void tktprinttext(TkText*);
14937da2899SCharles.Forsyth extern void tktprintindex(TkTindex*);
15037da2899SCharles.Forsyth extern void tktprintitem(TkTitem*);
15137da2899SCharles.Forsyth extern void tktprintline(TkTline*);
15237da2899SCharles.Forsyth extern void tktcheck(TkText*, char*);
15337da2899SCharles.Forsyth extern int tktutfpos(char *, int);
15437da2899SCharles.Forsyth 
15537da2899SCharles.Forsyth char*
tktext(TkTop * t,char * arg,char ** ret)15637da2899SCharles.Forsyth tktext(TkTop *t, char* arg, char **ret)
15737da2899SCharles.Forsyth {
15837da2899SCharles.Forsyth 	Tk *tk;
15937da2899SCharles.Forsyth 	char *e;
16037da2899SCharles.Forsyth 	TkEnv *ev;
16137da2899SCharles.Forsyth 	TkTline *l;
16237da2899SCharles.Forsyth 	TkTitem *it = nil;
16337da2899SCharles.Forsyth 	TkName *names = nil;
16437da2899SCharles.Forsyth 	TkTtaginfo *ti = nil;
16537da2899SCharles.Forsyth 	TkOptab tko[3];
16637da2899SCharles.Forsyth 	TkTmarkinfo *mi = nil;
16737da2899SCharles.Forsyth 	TkText *tkt, *tktshare;
16837da2899SCharles.Forsyth 
16937da2899SCharles.Forsyth 	tk = tknewobj(t, TKtext, sizeof(Tk)+sizeof(TkText));
17037da2899SCharles.Forsyth 	if(tk == nil)
17137da2899SCharles.Forsyth 		return TkNomem;
17237da2899SCharles.Forsyth 
17337da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
17437da2899SCharles.Forsyth 
17537da2899SCharles.Forsyth 	tk->relief = TKsunken;
176c9ccdbd5Sforsyth 	tk->borderwidth = 1;
17737da2899SCharles.Forsyth 	tk->ipad.x = Textpadx * 2;
17837da2899SCharles.Forsyth 	tk->ipad.y = Textpady * 2;
17937da2899SCharles.Forsyth 	tk->flag |= Tktakefocus;
18037da2899SCharles.Forsyth 	tkt->sborderwidth = 0;
18137da2899SCharles.Forsyth 	tkt->inswidth = 2;
18237da2899SCharles.Forsyth 	tkt->cur_flag = 0;	/* text cursor doesn't show up initially */
18337da2899SCharles.Forsyth 	tkt->opts[TkTwrap] = Tkwrapchar;
18437da2899SCharles.Forsyth 	tkt->opts[TkTrelief] = TKflat;
18537da2899SCharles.Forsyth 	tkt->opts[TkTjustify] = Tkleft;
18637da2899SCharles.Forsyth 	tkt->propagate = BoolX;
18737da2899SCharles.Forsyth 
18837da2899SCharles.Forsyth 	tko[0].ptr = tk;
18937da2899SCharles.Forsyth 	tko[0].optab = tkgeneric;
19037da2899SCharles.Forsyth 	tko[1].ptr = tkt;
19137da2899SCharles.Forsyth 	tko[1].optab = textopts;
19237da2899SCharles.Forsyth 	tko[2].ptr = nil;
19337da2899SCharles.Forsyth 
19437da2899SCharles.Forsyth 	tk->req.width = tk->env->wzero*Textwidth;
19537da2899SCharles.Forsyth 	tk->req.height = tk->env->font->height*Textheight;
19637da2899SCharles.Forsyth 
19737da2899SCharles.Forsyth 	names = nil;
19837da2899SCharles.Forsyth 	e = tkparse(t, arg, tko, &names);
19937da2899SCharles.Forsyth 	if(e != nil)
20037da2899SCharles.Forsyth 		goto err;
20137da2899SCharles.Forsyth 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
20237da2899SCharles.Forsyth 	if(names == nil) {
20337da2899SCharles.Forsyth 		/* tkerr(t, arg); XXX */
20437da2899SCharles.Forsyth 		e = TkBadwp;
20537da2899SCharles.Forsyth 		goto err;
20637da2899SCharles.Forsyth 	}
20737da2899SCharles.Forsyth 
20837da2899SCharles.Forsyth 	if(tkt->tagshare != nil) {
20937da2899SCharles.Forsyth 		tkputenv(tk->env);
21037da2899SCharles.Forsyth 		tk->env = tkt->tagshare->env;
21137da2899SCharles.Forsyth 		tk->env->ref++;
21237da2899SCharles.Forsyth 	}
21337da2899SCharles.Forsyth 
21437da2899SCharles.Forsyth 	if(tk->flag&Tkdisabled)
21537da2899SCharles.Forsyth 		tkt->inswidth = 0;
21637da2899SCharles.Forsyth 
21737da2899SCharles.Forsyth 	if(tkt->tabs == nil) {
21837da2899SCharles.Forsyth 		tkt->tabs = malloc(sizeof(TkTtabstop));
21937da2899SCharles.Forsyth 		if(tkt->tabs == nil)
22037da2899SCharles.Forsyth 			goto err;
22137da2899SCharles.Forsyth 		tkt->tabs->pos = 8*tk->env->wzero;
22237da2899SCharles.Forsyth 		tkt->tabs->justify = Tkleft;
22337da2899SCharles.Forsyth 		tkt->tabs->next = nil;
22437da2899SCharles.Forsyth 	}
22537da2899SCharles.Forsyth 
22637da2899SCharles.Forsyth 	if(tkt->tagshare != nil) {
22737da2899SCharles.Forsyth 		tktshare = TKobj(TkText, tkt->tagshare);
22837da2899SCharles.Forsyth 		tkt->tags = tktshare->tags;
22937da2899SCharles.Forsyth 		tkt->nexttag = tktshare->nexttag;
23037da2899SCharles.Forsyth 	}
23137da2899SCharles.Forsyth 	else {
23237da2899SCharles.Forsyth 		/* Note: sel should have id == TkTselid == 0 */
23337da2899SCharles.Forsyth 		e = tktaddtaginfo(tk, "sel", &ti);
23437da2899SCharles.Forsyth 		if(e != nil)
23537da2899SCharles.Forsyth 			goto err;
23637da2899SCharles.Forsyth 
23737da2899SCharles.Forsyth 		tkputenv(ti->env);
23837da2899SCharles.Forsyth 		ti->env = tknewenv(t);
23937da2899SCharles.Forsyth 		if(ti->env == nil)
24037da2899SCharles.Forsyth 			goto err;
24137da2899SCharles.Forsyth 
24237da2899SCharles.Forsyth 		ev = ti->env;
24337da2899SCharles.Forsyth 		ev->colors[TkCbackgnd] = tk->env->colors[TkCselectbgnd];
24437da2899SCharles.Forsyth 		ev->colors[TkCbackgndlght] = tk->env->colors[TkCselectbgndlght];
24537da2899SCharles.Forsyth 		ev->colors[TkCbackgnddark] = tk->env->colors[TkCselectbgnddark];
24637da2899SCharles.Forsyth 		ev->colors[TkCforegnd] = tk->env->colors[TkCselectfgnd];
24737da2899SCharles.Forsyth 		ev->set = (1<<TkCbackgnd)|(1<<TkCbackgndlght)|
24837da2899SCharles.Forsyth 			  (1<<TkCbackgnddark)|(1<<TkCforegnd);
24937da2899SCharles.Forsyth 
25037da2899SCharles.Forsyth 		ti->opts[TkTborderwidth] = tkt->sborderwidth;
25137da2899SCharles.Forsyth 		if(tkt->sborderwidth > 0)
25237da2899SCharles.Forsyth 			ti->opts[TkTrelief] = TKraised;
25337da2899SCharles.Forsyth 	}
25437da2899SCharles.Forsyth 
25537da2899SCharles.Forsyth 	e = tktaddmarkinfo(tkt, "current", &mi);
25637da2899SCharles.Forsyth 	if(e != nil)
25737da2899SCharles.Forsyth 		goto err;
25837da2899SCharles.Forsyth 
25937da2899SCharles.Forsyth 	e = tktaddmarkinfo(tkt, "insert", &mi);
26037da2899SCharles.Forsyth 	if(e != nil)
26137da2899SCharles.Forsyth 		goto err;
26237da2899SCharles.Forsyth 
26337da2899SCharles.Forsyth 	tkt->start.flags = TkTfirst|TkTlast;
26437da2899SCharles.Forsyth 	tkt->end.flags = TkTlast;
26537da2899SCharles.Forsyth 
26637da2899SCharles.Forsyth 	e = tktnewitem(TkTnewline, 0, &it);
26737da2899SCharles.Forsyth 
26837da2899SCharles.Forsyth 	if(e != nil)
26937da2899SCharles.Forsyth 		goto err;
27037da2899SCharles.Forsyth 
27137da2899SCharles.Forsyth 	e = tktnewline(TkTfirst|TkTlast, it, &tkt->start, &tkt->end, &l);
27237da2899SCharles.Forsyth 	if(e != nil)
27337da2899SCharles.Forsyth 		goto err;
27437da2899SCharles.Forsyth 
27537da2899SCharles.Forsyth 	e = tktnewitem(TkTmark, 0, &it);
27637da2899SCharles.Forsyth 	if(e != nil)
27737da2899SCharles.Forsyth 		goto err;
27837da2899SCharles.Forsyth 
27937da2899SCharles.Forsyth 	it->next = l->items;
28037da2899SCharles.Forsyth 	l->items = it;
28137da2899SCharles.Forsyth 	it->imark = mi;
28237da2899SCharles.Forsyth 	mi->cur = it;
28337da2899SCharles.Forsyth 	tkt->nlines = 1;
28437da2899SCharles.Forsyth 	tkt->scrolltop[Tkvertical] = -1;
28537da2899SCharles.Forsyth 	tkt->scrolltop[Tkhorizontal] = -1;
28637da2899SCharles.Forsyth 	tkt->scrollbot[Tkvertical] = -1;
28737da2899SCharles.Forsyth 	tkt->scrollbot[Tkhorizontal] = -1;
28837da2899SCharles.Forsyth 
28937da2899SCharles.Forsyth 	if(tkt->tagshare != nil)
29037da2899SCharles.Forsyth 		tk->binds = tkt->tagshare->binds;
29137da2899SCharles.Forsyth 	else {
29237da2899SCharles.Forsyth 		e = tkbindings(t, tk, tktbinds, nelem(tktbinds));
29337da2899SCharles.Forsyth 
29437da2899SCharles.Forsyth 		if(e != nil)
29537da2899SCharles.Forsyth 			goto err;
29637da2899SCharles.Forsyth 	}
29737da2899SCharles.Forsyth 	if (tkt->propagate == BoolT) {
29837da2899SCharles.Forsyth 		if ((tk->flag & Tksetwidth) == 0)
29937da2899SCharles.Forsyth 			tk->req.width = tktmaxwid(tkt->start.next);
30037da2899SCharles.Forsyth 		if ((tk->flag & Tksetheight) == 0)
30137da2899SCharles.Forsyth 			tk->req.height = tkt->end.orig.y;
30237da2899SCharles.Forsyth 	}
30337da2899SCharles.Forsyth 
30437da2899SCharles.Forsyth 	e = tkaddchild(t, tk, &names);
30537da2899SCharles.Forsyth 	tkfreename(names);
30637da2899SCharles.Forsyth 	if(e != nil)
30737da2899SCharles.Forsyth 		goto err;
30837da2899SCharles.Forsyth 	tk->name->link = nil;
30937da2899SCharles.Forsyth 
31037da2899SCharles.Forsyth 	return tkvalue(ret, "%s", tk->name->name);
31137da2899SCharles.Forsyth 
31237da2899SCharles.Forsyth err:
31337da2899SCharles.Forsyth 	/* XXX it's possible there's a memory leak here */
31437da2899SCharles.Forsyth 	tkfreeobj(tk);
31537da2899SCharles.Forsyth 	return e;
31637da2899SCharles.Forsyth }
31737da2899SCharles.Forsyth 
31837da2899SCharles.Forsyth /*
31937da2899SCharles.Forsyth  * There are four coordinate systems of interest:
32037da2899SCharles.Forsyth  *	S - screen coordinate system (i.e. top left corner of
32137da2899SCharles.Forsyth  *		inferno screen is (0,0) in S space.)
32237da2899SCharles.Forsyth  *	I - image coordinate system (i.e. top left corner of
32337da2899SCharles.Forsyth  *		tkimageof(this widget) is (0,0) in I space.)
32437da2899SCharles.Forsyth  *	T - text coordinate system (i.e., top left of first line
32537da2899SCharles.Forsyth  *		is at (0,0) in T space.)
32637da2899SCharles.Forsyth  *	V - view coordinate system (i.e., top left of visible
32737da2899SCharles.Forsyth  *		portion of widget is at (0,0) in V space.)
32837da2899SCharles.Forsyth  *
32937da2899SCharles.Forsyth  *	A point P in the four systems (Ps, Pi, Pt, Pv) satisfies:
33037da2899SCharles.Forsyth  *		Pt = Ps - deltast
33137da2899SCharles.Forsyth  *		Pv = Ps - deltasv
33237da2899SCharles.Forsyth  *		Pv = Pi - deltaiv
33337da2899SCharles.Forsyth  *	(where deltast is vector from S origin to T origin;
33437da2899SCharles.Forsyth  *	     deltasv is vector from S origin to V origin;
33537da2899SCharles.Forsyth  *	     deltaiv is vector from I origin to V origin)
33637da2899SCharles.Forsyth  *
33737da2899SCharles.Forsyth  *	We keep deltatv, deltasv, and deltaiv in tkt.
33837da2899SCharles.Forsyth  *	Deltatv is updated by scrolling.
33937da2899SCharles.Forsyth  *	Deltasv is updated by geom changes:
34037da2899SCharles.Forsyth  *		tkposn(tk)+ipad/2
34137da2899SCharles.Forsyth  *	Deltaiv is affected by geom changes and the call to the draw function:
34237da2899SCharles.Forsyth  *		tk->act+orig+ipad/2+(bw,bw) (orig is the parameter to tkdrawtext),
34337da2899SCharles.Forsyth  *
34437da2899SCharles.Forsyth  *	We can derive
34537da2899SCharles.Forsyth  *		Ps = Pt + deltast
34637da2899SCharles.Forsyth  *		   = Pt +  deltasv - deltatv
34737da2899SCharles.Forsyth  *
34837da2899SCharles.Forsyth  *		Pv = Pt - deltatv
34937da2899SCharles.Forsyth  *
35037da2899SCharles.Forsyth  * Here are various coordinates in the text widget according
35137da2899SCharles.Forsyth  * to which coordinate system they use:
35237da2899SCharles.Forsyth  *
35337da2899SCharles.Forsyth  *	S - Mouse coordinates (coming in to tktextevent);
35437da2899SCharles.Forsyth  *		the deltasv parameter to tkdrawtext;
35537da2899SCharles.Forsyth  *		coords in tkt->image, where drawing is done to
35637da2899SCharles.Forsyth  *		(to get same bit-alignment as screen, for fast transfer)
35737da2899SCharles.Forsyth  *	T - orig in TkTlines
35837da2899SCharles.Forsyth  *	V - %x,%y delivered via binds to TkText or its tags
35937da2899SCharles.Forsyth 
36037da2899SCharles.Forsyth  * Note deltasv changes underneath us, so is calculated on the fly
36137da2899SCharles.Forsyth  * when it needs to be (in tktextevent).
36237da2899SCharles.Forsyth  *
36337da2899SCharles.Forsyth  */
36437da2899SCharles.Forsyth static void
tktsetdeltas(Tk * tk,Point orig)36537da2899SCharles.Forsyth tktsetdeltas(Tk *tk, Point orig)
36637da2899SCharles.Forsyth {
36737da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
36837da2899SCharles.Forsyth 
36937da2899SCharles.Forsyth 	tkt->deltaiv.x = orig.x + tk->act.x + tk->ipad.x/2 + tk->borderwidth;
37037da2899SCharles.Forsyth 	tkt->deltaiv.y = orig.y + tk->act.y + tk->ipad.y/2 + tk->borderwidth;
37137da2899SCharles.Forsyth }
37237da2899SCharles.Forsyth 
37337da2899SCharles.Forsyth static Point
tktrelpos(Tk * sub)37437da2899SCharles.Forsyth tktrelpos(Tk *sub)
37537da2899SCharles.Forsyth {
37637da2899SCharles.Forsyth 	Tk *tk;
37737da2899SCharles.Forsyth 	TkTindex ix;
37837da2899SCharles.Forsyth 	Rectangle r;
37937da2899SCharles.Forsyth 	Point ans;
38037da2899SCharles.Forsyth 
38137da2899SCharles.Forsyth 	tk = sub->parent;
38237da2899SCharles.Forsyth 	if(tk == nil)
38337da2899SCharles.Forsyth 		return ZP;
38437da2899SCharles.Forsyth 
38537da2899SCharles.Forsyth 	if(tktfindsubitem(sub, &ix)) {
38637da2899SCharles.Forsyth 		r = tktbbox(tk, &ix);
38737da2899SCharles.Forsyth 		ans.x = r.min.x;
38837da2899SCharles.Forsyth 		ans.y = r.min.y;
38937da2899SCharles.Forsyth 		return r.min;
39037da2899SCharles.Forsyth 	}
39137da2899SCharles.Forsyth 	return ZP;
39237da2899SCharles.Forsyth }
39337da2899SCharles.Forsyth 
39437da2899SCharles.Forsyth static void
tktreplclipr(Image * dst,Rectangle r)39537da2899SCharles.Forsyth tktreplclipr(Image *dst, Rectangle r)
39637da2899SCharles.Forsyth {
39737da2899SCharles.Forsyth 	int locked;
39837da2899SCharles.Forsyth 
39937da2899SCharles.Forsyth 	locked = lockdisplay(dst->display);
40037da2899SCharles.Forsyth 	replclipr(dst, 0, r);
40137da2899SCharles.Forsyth 	if(locked)
40237da2899SCharles.Forsyth 		unlockdisplay(dst->display);
40337da2899SCharles.Forsyth }
40437da2899SCharles.Forsyth 
40537da2899SCharles.Forsyth char*
tkdrawtext(Tk * tk,Point orig)40637da2899SCharles.Forsyth tkdrawtext(Tk *tk, Point orig)
40737da2899SCharles.Forsyth {
40837da2899SCharles.Forsyth 	int vh;
40937da2899SCharles.Forsyth 	Image *dst;
41037da2899SCharles.Forsyth 	TkText *tkt;
41137da2899SCharles.Forsyth 	TkTline *l, *lend;
41237da2899SCharles.Forsyth 	Point p, deltait;
41337da2899SCharles.Forsyth 	Rectangle oclipr;
41437da2899SCharles.Forsyth 	int reldone = 1;
41537da2899SCharles.Forsyth 	char *e;
41637da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
41737da2899SCharles.Forsyth 	dst = tkimageof(tk);
41837da2899SCharles.Forsyth 	if (dst == nil)
41937da2899SCharles.Forsyth 		return nil;
42037da2899SCharles.Forsyth 	tkt->image = dst;
42137da2899SCharles.Forsyth 	tktsetdeltas(tk, orig);
42237da2899SCharles.Forsyth 	tkt->tflag |= TkTdrawn|TkTdlocked;
42337da2899SCharles.Forsyth 	oclipr = dst->clipr;
42437da2899SCharles.Forsyth 	tktsetclip(tk);
42537da2899SCharles.Forsyth 
42637da2899SCharles.Forsyth 	if(tk->flag&Tkrefresh) {
42737da2899SCharles.Forsyth 		reldone = 0;
42837da2899SCharles.Forsyth 		tktnotdrawn(tk, 0, tkt->end.orig.y, 1);
42937da2899SCharles.Forsyth 	}
43037da2899SCharles.Forsyth 	tk->flag &= ~Tkrefresh;
43137da2899SCharles.Forsyth 
43237da2899SCharles.Forsyth 	deltait = subpt(tkt->deltaiv, tkt->deltatv);
43337da2899SCharles.Forsyth 	vh = tk->act.height - tk->ipad.y/2;
43437da2899SCharles.Forsyth 	lend = &tkt->end;
43537da2899SCharles.Forsyth 	for(l = tkt->start.next; l != lend; l = l->next) {
43637da2899SCharles.Forsyth 		if(l->orig.y+l->height < tkt->deltatv.y)
43737da2899SCharles.Forsyth 			continue;
43837da2899SCharles.Forsyth 		if(l->orig.y > tkt->deltatv.y + vh)
43937da2899SCharles.Forsyth 			break;
44037da2899SCharles.Forsyth 		if(!(l->flags&TkTdrawn)) {
44137da2899SCharles.Forsyth 			e = tktdrawline(dst, tk, l, deltait);
44237da2899SCharles.Forsyth 			if(e != nil)
44337da2899SCharles.Forsyth 				return e;
44437da2899SCharles.Forsyth 		}
44537da2899SCharles.Forsyth 	}
44637da2899SCharles.Forsyth 
44737da2899SCharles.Forsyth 	tktreplclipr(dst, oclipr);
44837da2899SCharles.Forsyth 	if(!reldone) {
44937da2899SCharles.Forsyth 		p.x = orig.x + tk->act.x;
45037da2899SCharles.Forsyth 		p.y = orig.y + tk->act.y;
45137da2899SCharles.Forsyth 		tkdrawrelief(dst, tk, p, TkCbackgnd, tk->relief);
45237da2899SCharles.Forsyth 	}
45337da2899SCharles.Forsyth 	tkt->tflag &= ~TkTdlocked;
45437da2899SCharles.Forsyth 
45537da2899SCharles.Forsyth 	return nil;
45637da2899SCharles.Forsyth }
45737da2899SCharles.Forsyth 
45837da2899SCharles.Forsyth /*
45937da2899SCharles.Forsyth  * Set the clipping rectangle of the destination image to the
46037da2899SCharles.Forsyth  * intersection of the current clipping rectangle and the area inside
46137da2899SCharles.Forsyth  * the text widget that needs to be redrawn.
46237da2899SCharles.Forsyth  * The caller should save the old one and restore it later.
46337da2899SCharles.Forsyth  */
46437da2899SCharles.Forsyth static void
tktsetclip(Tk * tk)46537da2899SCharles.Forsyth tktsetclip(Tk *tk)
46637da2899SCharles.Forsyth {
46737da2899SCharles.Forsyth 	Rectangle r;
46837da2899SCharles.Forsyth 	Image *dst;
46937da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
47037da2899SCharles.Forsyth 
47137da2899SCharles.Forsyth 	dst = tkt->image;
47237da2899SCharles.Forsyth 	r.min = tkt->deltaiv;
47337da2899SCharles.Forsyth 	r.max.x = r.min.x + tk->act.width - tk->ipad.x / 2;
47437da2899SCharles.Forsyth 	r.max.y = r.min.y + tk->act.height - tk->ipad.y / 2;
47537da2899SCharles.Forsyth 
47637da2899SCharles.Forsyth 	if(!rectclip(&r, dst->clipr))
47737da2899SCharles.Forsyth 		r.max = r.min;
47837da2899SCharles.Forsyth 	tktreplclipr(dst, r);
47937da2899SCharles.Forsyth }
48037da2899SCharles.Forsyth 
48137da2899SCharles.Forsyth static char*
tktdrawline(Image * i,Tk * tk,TkTline * l,Point deltait)48237da2899SCharles.Forsyth tktdrawline(Image *i, Tk *tk, TkTline *l, Point deltait)
48337da2899SCharles.Forsyth {
48437da2899SCharles.Forsyth 	Tk *sub;
48537da2899SCharles.Forsyth 	Font *f;
48637da2899SCharles.Forsyth 	Image *bg;
48737da2899SCharles.Forsyth 	Point p, q;
48837da2899SCharles.Forsyth 	Rectangle r;
48937da2899SCharles.Forsyth 	TkText *tkt;
49037da2899SCharles.Forsyth 	TkTitem *it, *z;
49137da2899SCharles.Forsyth 	int bevtop, bevbot;
49237da2899SCharles.Forsyth 	TkEnv *e, *et, *env;
49337da2899SCharles.Forsyth 	int *opts;
49437da2899SCharles.Forsyth 	int o, bd, ul, ov, h, w, la, lh, cursorx, join;
49537da2899SCharles.Forsyth 	char *err;
49637da2899SCharles.Forsyth 
49737da2899SCharles.Forsyth 	env = mallocz(sizeof(TkEnv), 0);
49837da2899SCharles.Forsyth 	if(env == nil)
49937da2899SCharles.Forsyth 		return TkNomem;
50037da2899SCharles.Forsyth 	opts = mallocz(TkTnumopts*sizeof(int), 0);
50137da2899SCharles.Forsyth 	if(opts == nil) {
50237da2899SCharles.Forsyth 		free(env);
50337da2899SCharles.Forsyth 		return TkNomem;
50437da2899SCharles.Forsyth 	}
50537da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
50637da2899SCharles.Forsyth 	e = tk->env;
50737da2899SCharles.Forsyth 	et = env;
50837da2899SCharles.Forsyth 	et->top = e->top;
50937da2899SCharles.Forsyth 	f = e->font;
51037da2899SCharles.Forsyth 
51137da2899SCharles.Forsyth 	/* l->orig is in T space, p is in I space */
51237da2899SCharles.Forsyth 	la = l->ascent;
51337da2899SCharles.Forsyth 	lh = l->height;
51437da2899SCharles.Forsyth 	p = addpt(l->orig, deltait);
51537da2899SCharles.Forsyth 	p.y += la;
51637da2899SCharles.Forsyth /* if(tktdbg){print("drawline, p=(%d,%d), f->a=%d, f->h=%d\n", p.x, p.y, f->ascent, f->height); tktprintline(l);} */
51737da2899SCharles.Forsyth 	cursorx = -1000;
51837da2899SCharles.Forsyth 	join = 0;
51937da2899SCharles.Forsyth 	for(it = l->items; it != nil; it = it->next) {
52037da2899SCharles.Forsyth 		bg = tkgc(e, TkCbackgnd);
52137da2899SCharles.Forsyth 		if(tktanytags(it)) {
52237da2899SCharles.Forsyth 			tkttagopts(tk, it, opts, env, nil, 1);
52337da2899SCharles.Forsyth 			if(e->colors[TkCbackgnd] != et->colors[TkCbackgnd]) {
52437da2899SCharles.Forsyth 				bg = tkgc(et, TkCbackgnd);
52537da2899SCharles.Forsyth 				r.min = p;
52637da2899SCharles.Forsyth 				r.min.y -= la;
52737da2899SCharles.Forsyth 				r.max.x = r.min.x + it->width;
52837da2899SCharles.Forsyth 				r.max.y = r.min.y + lh;
52937da2899SCharles.Forsyth 				draw(i, r, bg, nil, ZP);
53037da2899SCharles.Forsyth 			}
53137da2899SCharles.Forsyth 			o = opts[TkTrelief];
53237da2899SCharles.Forsyth 			bd = opts[TkTborderwidth];
53337da2899SCharles.Forsyth 			if((o == TKsunken || o == TKraised) && bd > 0) {
53437da2899SCharles.Forsyth 				/* fit relief inside item bounding box */
53537da2899SCharles.Forsyth 
53637da2899SCharles.Forsyth 				q.x = p.x;
53737da2899SCharles.Forsyth 				q.y = p.y - la;
53837da2899SCharles.Forsyth 				if(it->width < 2*bd)
53937da2899SCharles.Forsyth 					bd = it->width / 2;
54037da2899SCharles.Forsyth 				if(lh < 2*bd)
54137da2899SCharles.Forsyth 					bd = lh / 2;
54237da2899SCharles.Forsyth 				w = it->width - 2*bd;
54337da2899SCharles.Forsyth 				h = lh - 2*bd;
54437da2899SCharles.Forsyth 				if(o == TKraised) {
54537da2899SCharles.Forsyth 					bevtop = TkLightshade;
54637da2899SCharles.Forsyth 					bevbot = TkDarkshade;
54737da2899SCharles.Forsyth 				}
54837da2899SCharles.Forsyth 				else {
54937da2899SCharles.Forsyth 					bevtop = TkDarkshade;
55037da2899SCharles.Forsyth 					bevbot = TkLightshade;
55137da2899SCharles.Forsyth 				}
55237da2899SCharles.Forsyth 
55337da2899SCharles.Forsyth 				tkbevel(i, q, w, h, bd,
55437da2899SCharles.Forsyth 					tkgc(et, TkCbackgnd+bevtop), tkgc(et, TkCbackgnd+bevbot));
55537da2899SCharles.Forsyth 
55637da2899SCharles.Forsyth 				/* join relief between adjacent items if tags match */
55737da2899SCharles.Forsyth 				if(join) {
55837da2899SCharles.Forsyth 					r.min.x = q.x;
55937da2899SCharles.Forsyth 					r.max.x = q.x + bd;
56037da2899SCharles.Forsyth 					r.min.y = q.y + bd;
56137da2899SCharles.Forsyth 					r.max.y = r.min.y + h;
56237da2899SCharles.Forsyth 					draw(i, r, bg, nil, ZP);
56337da2899SCharles.Forsyth 					r.min.y = r.max.y;
56437da2899SCharles.Forsyth 					r.max.y = r.min.y + bd;
56537da2899SCharles.Forsyth 					draw(i, r, tkgc(et, TkCbackgnd+bevbot), nil, ZP);
56637da2899SCharles.Forsyth 				}
56737da2899SCharles.Forsyth 				for(z = it->next; z != nil && z->kind == TkTmark; )
56837da2899SCharles.Forsyth 					z = z->next;
56937da2899SCharles.Forsyth 				if(z != nil && tktsametags(z, it)) {
57037da2899SCharles.Forsyth 					r.min.x = q.x + bd + w;
57137da2899SCharles.Forsyth 					r.max.x = r.min.x + bd;
57237da2899SCharles.Forsyth 					r.min.y = q.y;
57337da2899SCharles.Forsyth 					r.max.y = q.y + bd;
57437da2899SCharles.Forsyth 					draw(i, r, tkgc(et, TkCbackgnd+bevtop), nil, ZP);
57537da2899SCharles.Forsyth 					r.min.y = r.max.y;
57637da2899SCharles.Forsyth 					r.max.y = r.min.y + h;
57737da2899SCharles.Forsyth 					draw(i, r, bg, nil, ZP);
57837da2899SCharles.Forsyth 					join = 1;
57937da2899SCharles.Forsyth 				}
58037da2899SCharles.Forsyth 				else
58137da2899SCharles.Forsyth 					join = 0;
58237da2899SCharles.Forsyth 			}
58337da2899SCharles.Forsyth 			o = opts[TkToffset];
58437da2899SCharles.Forsyth 			ul = opts[TkTunderline];
58537da2899SCharles.Forsyth 			ov = opts[TkToverstrike];
58637da2899SCharles.Forsyth 		}
58737da2899SCharles.Forsyth 		else {
58837da2899SCharles.Forsyth 			et->font = f;
58937da2899SCharles.Forsyth 			et->colors[TkCforegnd] = e->colors[TkCforegnd];
59037da2899SCharles.Forsyth 			o = 0;
59137da2899SCharles.Forsyth 			ul = 0;
59237da2899SCharles.Forsyth 			ov = 0;
59337da2899SCharles.Forsyth 		}
59437da2899SCharles.Forsyth 
59537da2899SCharles.Forsyth 		switch(it->kind) {
59637da2899SCharles.Forsyth 		case TkTascii:
59737da2899SCharles.Forsyth 		case TkTrune:
59837da2899SCharles.Forsyth 			q.x = p.x;
59937da2899SCharles.Forsyth 			q.y = p.y - env->font->ascent - o;
60037da2899SCharles.Forsyth /*if(tktdbg)print("q=(%d,%d)\n", q.x, q.y);*/
60137da2899SCharles.Forsyth 			string(i, q, tkgc(et, TkCforegnd), q, env->font, it->istring);
60237da2899SCharles.Forsyth 			if(ov == BoolT) {
60337da2899SCharles.Forsyth 				r.min.x = q.x;
60437da2899SCharles.Forsyth 				r.max.x = r.min.x + it->width;
60537da2899SCharles.Forsyth 				r.min.y = q.y + 2*env->font->ascent/3;
60637da2899SCharles.Forsyth 				r.max.y = r.min.y + 2;
60737da2899SCharles.Forsyth 				draw(i, r, tkgc(et, TkCforegnd), nil, ZP);
60837da2899SCharles.Forsyth 			}
60937da2899SCharles.Forsyth 			if(ul == BoolT) {
61037da2899SCharles.Forsyth 				r.min.x = q.x;
61137da2899SCharles.Forsyth 				r.max.x = r.min.x + it->width;
61237da2899SCharles.Forsyth 				r.max.y = p.y - la + lh;
61337da2899SCharles.Forsyth 				r.min.y = r.max.y - 2;
61437da2899SCharles.Forsyth 				draw(i, r, tkgc(et, TkCforegnd), nil, ZP);
61537da2899SCharles.Forsyth 			}
61637da2899SCharles.Forsyth 			break;
61737da2899SCharles.Forsyth 		case TkTmark:
61837da2899SCharles.Forsyth 			if((it->imark != nil)
61937da2899SCharles.Forsyth                            && strcmp(it->imark->name, "insert") == 0) {
62037da2899SCharles.Forsyth 				cursorx = p.x - 1;
62137da2899SCharles.Forsyth 			}
62237da2899SCharles.Forsyth 			break;
62337da2899SCharles.Forsyth 		case TkTwin:
62437da2899SCharles.Forsyth 			sub = it->iwin->sub;
62537da2899SCharles.Forsyth 			if(sub != nil) {
62637da2899SCharles.Forsyth 				int dirty;
62737da2899SCharles.Forsyth 				sub->flag |= Tkrefresh;
62837da2899SCharles.Forsyth 				sub->dirty = tkrect(sub, 1);
62937da2899SCharles.Forsyth 				err = tkdrawslaves(sub, p, &dirty);
63037da2899SCharles.Forsyth 				if(err != nil) {
63137da2899SCharles.Forsyth 					free(opts);
63237da2899SCharles.Forsyth 					free(env);
63337da2899SCharles.Forsyth 					return err;
63437da2899SCharles.Forsyth 				}
63537da2899SCharles.Forsyth 			}
63637da2899SCharles.Forsyth 			break;
63737da2899SCharles.Forsyth 		}
63837da2899SCharles.Forsyth 		p.x += it->width;
63937da2899SCharles.Forsyth 	}
64037da2899SCharles.Forsyth 	l->flags |= TkTdrawn;
64137da2899SCharles.Forsyth 
64237da2899SCharles.Forsyth 	/* do cursor last, so not overwritten by later items */
64337da2899SCharles.Forsyth 	if(cursorx != -1000 && tkt->inswidth > 0) {
64437da2899SCharles.Forsyth 		r.min.x = cursorx;
64537da2899SCharles.Forsyth 		r.min.y = p.y - la;
64637da2899SCharles.Forsyth 		r.max.x = r.min.x + tkt->inswidth;
64737da2899SCharles.Forsyth 		r.max.y = r.min.y + lh;
64837da2899SCharles.Forsyth 		r = rectsubpt(r, deltait);
64937da2899SCharles.Forsyth 		if (!eqrect(tkt->cur_rec, r))
65037da2899SCharles.Forsyth 			blinkreset(tk);
65137da2899SCharles.Forsyth 		tkt->cur_rec = r;
65237da2899SCharles.Forsyth 		if(tkt->cur_flag)
65337da2899SCharles.Forsyth 			tktextcursordraw(tk, TkCforegnd);
65437da2899SCharles.Forsyth 	}
65537da2899SCharles.Forsyth 
65637da2899SCharles.Forsyth 	free(opts);
65737da2899SCharles.Forsyth 	free(env);
65837da2899SCharles.Forsyth 	return nil;
65937da2899SCharles.Forsyth }
66037da2899SCharles.Forsyth 
66137da2899SCharles.Forsyth static void
tktextcursordraw(Tk * tk,int color)66237da2899SCharles.Forsyth tktextcursordraw(Tk *tk, int color)
66337da2899SCharles.Forsyth {
66437da2899SCharles.Forsyth 	Rectangle r;
66537da2899SCharles.Forsyth 	TkText *tkt;
66637da2899SCharles.Forsyth 	Image *i;
66737da2899SCharles.Forsyth 
66837da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
66937da2899SCharles.Forsyth 
67037da2899SCharles.Forsyth 	r = rectaddpt(tkt->cur_rec, subpt(tkt->deltaiv, tkt->deltatv));
67137da2899SCharles.Forsyth 
67237da2899SCharles.Forsyth 	/* check the cursor with widget boundary */
67337da2899SCharles.Forsyth 	/* do nothing if entire cursor outside widget boundary */
67437da2899SCharles.Forsyth 	if( ! (	r.max.x < tkt->deltaiv.x ||
67537da2899SCharles.Forsyth 		r.min.x > tkt->deltaiv.x + tk->act.width ||
67637da2899SCharles.Forsyth 		r.max.y < tkt->deltaiv.y ||
67737da2899SCharles.Forsyth 		r.min.y > tkt->deltaiv.y + tk->act.height)) {
67837da2899SCharles.Forsyth 
67937da2899SCharles.Forsyth 		/* clip rectangle if extends beyond widget boundary */
68037da2899SCharles.Forsyth 		if (r.min.x < tkt->deltaiv.x)
68137da2899SCharles.Forsyth 			r.min.x = tkt->deltaiv.x;
68237da2899SCharles.Forsyth 		if (r.max.x > tkt->deltaiv.x + tk->act.width)
68337da2899SCharles.Forsyth 			r.max.x = tkt->deltaiv.x + tk->act.width;
68437da2899SCharles.Forsyth 		if (r.min.y < tkt->deltaiv.y)
68537da2899SCharles.Forsyth 			r.min.y = tkt->deltaiv.y;
68637da2899SCharles.Forsyth 		if (r.max.y > tkt->deltaiv.y + tk->act.height)
68737da2899SCharles.Forsyth 			r.max.y = tkt->deltaiv.y + tk->act.height;
68837da2899SCharles.Forsyth 		i = tkimageof(tk);
68937da2899SCharles.Forsyth 		if (i != nil)
69037da2899SCharles.Forsyth 			draw(i, r, tkgc(tk->env, color), nil, ZP);
69137da2899SCharles.Forsyth 	}
69237da2899SCharles.Forsyth }
69337da2899SCharles.Forsyth 
69437da2899SCharles.Forsyth static void
blinkreset(Tk * tk)69537da2899SCharles.Forsyth blinkreset(Tk *tk)
69637da2899SCharles.Forsyth {
69737da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
69837da2899SCharles.Forsyth 	if (!tkhaskeyfocus(tk) || tk->flag&Tkdisabled)
69937da2899SCharles.Forsyth 		return;
70037da2899SCharles.Forsyth 	tkt->cur_flag = 1;
70137da2899SCharles.Forsyth 	tkblinkreset(tk);
70237da2899SCharles.Forsyth }
70337da2899SCharles.Forsyth 
70437da2899SCharles.Forsyth static void
showcaret(Tk * tk,int on)70537da2899SCharles.Forsyth showcaret(Tk *tk, int on)
70637da2899SCharles.Forsyth {
70737da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
70837da2899SCharles.Forsyth 	TkTline *l, *lend;
70937da2899SCharles.Forsyth 	TkTitem *it;
71037da2899SCharles.Forsyth 
71137da2899SCharles.Forsyth 	tkt->cur_flag = on;
71237da2899SCharles.Forsyth 	lend = &tkt->end;
71337da2899SCharles.Forsyth 	for(l = tkt->start.next; l != lend; l = l->next) {
71437da2899SCharles.Forsyth 		for (it = l->items; it != nil; it = it->next) {
71537da2899SCharles.Forsyth 			if (it->kind == TkTmark && it->imark != nil &&
71637da2899SCharles.Forsyth 				    strcmp(it->imark->name, "insert") == 0) {
71737da2899SCharles.Forsyth 				if (on) {
71837da2899SCharles.Forsyth 					tktextcursordraw(tk, TkCforegnd);
71937da2899SCharles.Forsyth 					tk->dirty = tkrect(tk, 1);
72037da2899SCharles.Forsyth 				} else
72137da2899SCharles.Forsyth 					tktnotdrawn(tk, l->orig.y, l->orig.y+l->height, 0);
72237da2899SCharles.Forsyth 				tkdirty(tk);
72337da2899SCharles.Forsyth 				return;
72437da2899SCharles.Forsyth 			}
72537da2899SCharles.Forsyth 		}
72637da2899SCharles.Forsyth 	}
72737da2899SCharles.Forsyth }
72837da2899SCharles.Forsyth 
72937da2899SCharles.Forsyth char*
tktextcursor(Tk * tk,char * arg,char ** ret)73037da2899SCharles.Forsyth tktextcursor(Tk *tk, char* arg, char **ret)
73137da2899SCharles.Forsyth {
73237da2899SCharles.Forsyth 	int on = 0;
73337da2899SCharles.Forsyth 	USED(ret);
73437da2899SCharles.Forsyth 
73537da2899SCharles.Forsyth 	if (tk->flag&Tkdisabled)
73637da2899SCharles.Forsyth 		return nil;
73737da2899SCharles.Forsyth 
73837da2899SCharles.Forsyth 	if(strcmp(arg, " insert") == 0) {
73937da2899SCharles.Forsyth 		tkblink(tk, showcaret);
74037da2899SCharles.Forsyth 		on = 1;
74137da2899SCharles.Forsyth 	}
74237da2899SCharles.Forsyth 	else
74337da2899SCharles.Forsyth 		tkblink(nil, nil);
74437da2899SCharles.Forsyth 
74537da2899SCharles.Forsyth 	showcaret(tk, on);
74637da2899SCharles.Forsyth 	return nil;
74737da2899SCharles.Forsyth }
74837da2899SCharles.Forsyth 
74937da2899SCharles.Forsyth /*
75037da2899SCharles.Forsyth  * Insert string s just before ins, but don't worry about geometry values.
75137da2899SCharles.Forsyth  * Don't worry about doing wrapping correctly, but break long strings
75237da2899SCharles.Forsyth  * into pieces to avoid bad behavior in the wrapping code of tktfixgeom.
75337da2899SCharles.Forsyth  * If tagit != 0, use its tags, else use the intersection of tags of
75437da2899SCharles.Forsyth  * non cont or mark elements just before and just after insertion point.
75537da2899SCharles.Forsyth  * (At beginning and end of widget, just use the tags of one adjacent item).
75637da2899SCharles.Forsyth  * Keep *ins up-to-date.
75737da2899SCharles.Forsyth  */
75837da2899SCharles.Forsyth char*
tktinsert(Tk * tk,TkTindex * ins,char * s,TkTitem * tagit)75937da2899SCharles.Forsyth tktinsert(Tk *tk, TkTindex *ins, char *s, TkTitem *tagit)
76037da2899SCharles.Forsyth {
76137da2899SCharles.Forsyth 	int c, n, nextra, nmax, atend, atbeg;
76237da2899SCharles.Forsyth 	char *e, *p;
76337da2899SCharles.Forsyth 	Rune r;
76437da2899SCharles.Forsyth 	TkTindex iprev, inext;
76537da2899SCharles.Forsyth 	TkTitem *i, *utagit;
76637da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
76737da2899SCharles.Forsyth 
76837da2899SCharles.Forsyth 	e = tktsplititem(ins);
76937da2899SCharles.Forsyth 	if(e != nil)
77037da2899SCharles.Forsyth 		return e;
77137da2899SCharles.Forsyth 
77237da2899SCharles.Forsyth 	/* if no tags give, use intersection of previous and next char tags */
77337da2899SCharles.Forsyth 
77437da2899SCharles.Forsyth 	nextra = 0;
77537da2899SCharles.Forsyth 	n = tk->env->wzero;
77637da2899SCharles.Forsyth 	if(n <= 0)
77737da2899SCharles.Forsyth 		n = 8;
77837da2899SCharles.Forsyth 	nmax = tk->act.width - tk->ipad.x;
77937da2899SCharles.Forsyth 	if(nmax <= 0) {
78037da2899SCharles.Forsyth 		if (tkt->propagate != BoolT || (tk->flag & Tksetwidth))
78137da2899SCharles.Forsyth 			nmax = tk->req.width;
78237da2899SCharles.Forsyth 		if(nmax <= 0)
78337da2899SCharles.Forsyth 			nmax = 60*n;
78437da2899SCharles.Forsyth 	}
78537da2899SCharles.Forsyth 	nmax = (nmax + n - 1) / n;
78637da2899SCharles.Forsyth 	utagit = nil;
78737da2899SCharles.Forsyth 	if(tagit == nil) {
78837da2899SCharles.Forsyth 		inext = *ins;
78937da2899SCharles.Forsyth 		tktadjustind(tkt, TkTbycharstart, &inext);
79037da2899SCharles.Forsyth 		atend = (inext.item->next == nil && inext.line->next == &tkt->end);
79137da2899SCharles.Forsyth 		if(atend || tktanytags(inext.item)) {
79237da2899SCharles.Forsyth 			iprev = *ins;
79337da2899SCharles.Forsyth 			tktadjustind(tkt, TkTbycharback, &iprev);
79437da2899SCharles.Forsyth 			atbeg = (iprev.line->prev == &tkt->start && iprev.line->items == iprev.item);
79537da2899SCharles.Forsyth 			if(atbeg || tktanytags(iprev.item)) {
79637da2899SCharles.Forsyth 				nextra = 0;
79737da2899SCharles.Forsyth 				if(!atend)
79837da2899SCharles.Forsyth 					nextra = inext.item->tagextra;
79937da2899SCharles.Forsyth 				if(!atbeg && iprev.item->tagextra > nextra)
80037da2899SCharles.Forsyth 					nextra = iprev.item->tagextra;
80137da2899SCharles.Forsyth 				e = tktnewitem(TkTascii, nextra, &utagit);
80237da2899SCharles.Forsyth 				if(e != nil)
80337da2899SCharles.Forsyth 					return e;
80437da2899SCharles.Forsyth 				if(!atend) {
80537da2899SCharles.Forsyth 					tkttagcomb(utagit, inext.item, 1);
80637da2899SCharles.Forsyth 					if(!atbeg)
80737da2899SCharles.Forsyth 						tkttagcomb(utagit, iprev.item, 0);
80837da2899SCharles.Forsyth 				}
80937da2899SCharles.Forsyth 				else if(!atbeg)
81037da2899SCharles.Forsyth 					tkttagcomb(utagit, iprev.item, 1);
81137da2899SCharles.Forsyth 				tagit = utagit;
81237da2899SCharles.Forsyth 			}
81337da2899SCharles.Forsyth 		}
81437da2899SCharles.Forsyth 	}
81537da2899SCharles.Forsyth 	else
81637da2899SCharles.Forsyth 		nextra = tagit->tagextra;
81737da2899SCharles.Forsyth 
81837da2899SCharles.Forsyth 	while((c = *s) != '\0') {
81937da2899SCharles.Forsyth 		e = tktnewitem(TkTascii, nextra, &i);
82037da2899SCharles.Forsyth 		if(e != nil) {
82137da2899SCharles.Forsyth 			if(utagit != nil)
82237da2899SCharles.Forsyth 				free(utagit);
82337da2899SCharles.Forsyth 			return e;
82437da2899SCharles.Forsyth 		}
82537da2899SCharles.Forsyth 
82637da2899SCharles.Forsyth 		if(tagit != nil)
82737da2899SCharles.Forsyth 			tkttagcomb(i, tagit, 1);
82837da2899SCharles.Forsyth 
82937da2899SCharles.Forsyth 		if(c == '\n') {
83037da2899SCharles.Forsyth 			i->kind = TkTnewline;
83137da2899SCharles.Forsyth 			tkt->nlines++;
83237da2899SCharles.Forsyth 			s++;
83337da2899SCharles.Forsyth 		}
83437da2899SCharles.Forsyth 		else
83537da2899SCharles.Forsyth 		if(c == '\t') {
83637da2899SCharles.Forsyth 			i->kind = TkTtab;
83737da2899SCharles.Forsyth 			s++;
83837da2899SCharles.Forsyth 		}
83937da2899SCharles.Forsyth 		else {
84037da2899SCharles.Forsyth 			p = s;
84137da2899SCharles.Forsyth 			n = 0;
84237da2899SCharles.Forsyth 			i->kind = TkTascii;
84337da2899SCharles.Forsyth 			while(c != '\0' && c != '\n' && c != '\t' && n < nmax){
84437da2899SCharles.Forsyth 				s += chartorune(&r, s);
84537da2899SCharles.Forsyth 				c = *s;
84637da2899SCharles.Forsyth 				n++;
84737da2899SCharles.Forsyth 			}
84837da2899SCharles.Forsyth 			/*
84937da2899SCharles.Forsyth 			 * if more bytes than runes, then it's not all ascii, so create a TkTrune item
85037da2899SCharles.Forsyth 			 */
85137da2899SCharles.Forsyth 			if(s - p > n)
85237da2899SCharles.Forsyth 				i->kind = TkTrune;
85337da2899SCharles.Forsyth 			n = s - p;
85437da2899SCharles.Forsyth 			i->istring = malloc(n+1);
85537da2899SCharles.Forsyth 			if(i->istring == nil) {
85637da2899SCharles.Forsyth 				tktfreeitems(tkt, i, 1);
85737da2899SCharles.Forsyth 				if(utagit != nil)
85837da2899SCharles.Forsyth 					free(utagit);
85937da2899SCharles.Forsyth 				return TkNomem;
86037da2899SCharles.Forsyth 			}
86137da2899SCharles.Forsyth 			memmove(i->istring, p, n);
86237da2899SCharles.Forsyth 			i->istring[n] = '\0';
86337da2899SCharles.Forsyth 		}
86437da2899SCharles.Forsyth 		e = tktiteminsert(tkt, ins, i);
86537da2899SCharles.Forsyth 		if(e != nil) {
86637da2899SCharles.Forsyth 			if(utagit != nil)
86737da2899SCharles.Forsyth 				free(utagit);
86837da2899SCharles.Forsyth 			tktfreeitems(tkt, i, 1);
86937da2899SCharles.Forsyth 			return e;
87037da2899SCharles.Forsyth 		}
87137da2899SCharles.Forsyth 	}
87237da2899SCharles.Forsyth 
87337da2899SCharles.Forsyth 	if(utagit != nil)
87437da2899SCharles.Forsyth 		free(utagit);
87537da2899SCharles.Forsyth 	return nil;
87637da2899SCharles.Forsyth }
87737da2899SCharles.Forsyth 
87837da2899SCharles.Forsyth void
tktextsize(Tk * tk,int dogeom)87937da2899SCharles.Forsyth tktextsize(Tk *tk, int dogeom)
88037da2899SCharles.Forsyth {
88137da2899SCharles.Forsyth 	TkText *tkt;
88237da2899SCharles.Forsyth 	TkGeom g;
88337da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
88437da2899SCharles.Forsyth 	if (tkt->propagate == BoolT) {
88537da2899SCharles.Forsyth 		g = tk->req;
88637da2899SCharles.Forsyth 		if ((tk->flag & Tksetwidth) == 0)
88737da2899SCharles.Forsyth 			tk->req.width = tktmaxwid(tkt->start.next);
88837da2899SCharles.Forsyth 		if ((tk->flag & Tksetheight) == 0)
88937da2899SCharles.Forsyth 			tk->req.height = tkt->end.orig.y;
89037da2899SCharles.Forsyth 		if (dogeom)
89137da2899SCharles.Forsyth 			tkgeomchg(tk, &g, tk->borderwidth);
89237da2899SCharles.Forsyth 	}
89337da2899SCharles.Forsyth }
89437da2899SCharles.Forsyth 
89537da2899SCharles.Forsyth static int
maximum(int a,int b)89637da2899SCharles.Forsyth maximum(int a, int b)
89737da2899SCharles.Forsyth {
89837da2899SCharles.Forsyth 	if (a > b)
89937da2899SCharles.Forsyth 		return a;
90037da2899SCharles.Forsyth 	return b;
90137da2899SCharles.Forsyth }
90237da2899SCharles.Forsyth 
90337da2899SCharles.Forsyth /*
90437da2899SCharles.Forsyth  * For lines l1->next, ..., l2, fix up the geometry
90537da2899SCharles.Forsyth  * elements of constituent TkTlines and TkTitems.
90637da2899SCharles.Forsyth  * This involves doing proper line wrapping, and calculating item
90737da2899SCharles.Forsyth  * widths and positions.
90837da2899SCharles.Forsyth  * Also, merge any adjacent TkTascii/TkTrune items with the same tags.
90937da2899SCharles.Forsyth  * Finally, bump the y component of lines l2->next, ... end.
91037da2899SCharles.Forsyth  * l2 should not be tkt->end.
91137da2899SCharles.Forsyth  *
91237da2899SCharles.Forsyth  * if finalwidth is 0, we're trying to work out what the
91337da2899SCharles.Forsyth  * width and height should be. if propagation is off,
91437da2899SCharles.Forsyth  * it's irrelevant; otherwise it must assume that
91537da2899SCharles.Forsyth  * its desired width will be fulfilled, as the packer
91637da2899SCharles.Forsyth  * doesn't iterate...
91737da2899SCharles.Forsyth  *
91837da2899SCharles.Forsyth  * N.B. this function rearranges lines, merges and splits items.
91937da2899SCharles.Forsyth  * this means that in general the item and line pointed to
92037da2899SCharles.Forsyth  * by any index might have been freed after tktfixgeom
92137da2899SCharles.Forsyth  * has been called.
92237da2899SCharles.Forsyth  */
92337da2899SCharles.Forsyth char*
tktfixgeom(Tk * tk,TkTline * l1,TkTline * l2,int finalwidth)92437da2899SCharles.Forsyth tktfixgeom(Tk *tk, TkTline *l1, TkTline *l2, int finalwidth)
92537da2899SCharles.Forsyth {
92637da2899SCharles.Forsyth 	int x, y, a, wa, h, w, o, n, j, sp3, xleft, xright, winw, oa, oh, lh;
92737da2899SCharles.Forsyth 	int wrapmode, just, needsplit;
92837da2899SCharles.Forsyth 	char *e, *s;
92937da2899SCharles.Forsyth 	TkText *tkt;
93037da2899SCharles.Forsyth 	Tk *sub;
93137da2899SCharles.Forsyth 	TkTitem *i, *it, *ilast, *iprev;
93237da2899SCharles.Forsyth 	TkTindex ix, ixprev, ixw;
93337da2899SCharles.Forsyth 	TkTline *l, *lafter;
93437da2899SCharles.Forsyth 	Interval oldi, hole, rest, newrest;
93537da2899SCharles.Forsyth 	TkEnv *env;
93637da2899SCharles.Forsyth 	Font *f;
93737da2899SCharles.Forsyth 	int *opts;
93837da2899SCharles.Forsyth 	TkTtabstop *tb;
93937da2899SCharles.Forsyth 
94037da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
94137da2899SCharles.Forsyth 
94237da2899SCharles.Forsyth 	if(tktdbg)
94337da2899SCharles.Forsyth 		tktcheck(tkt, "tktfixgeom");
94437da2899SCharles.Forsyth 
94537da2899SCharles.Forsyth 	if (!finalwidth && tkt->propagate == BoolT) {
94637da2899SCharles.Forsyth 		if ((tk->flag & Tksetwidth) == 0)
94737da2899SCharles.Forsyth 			winw = 1000000;
94837da2899SCharles.Forsyth 		else
94937da2899SCharles.Forsyth 			winw = tk->req.width;
95037da2899SCharles.Forsyth 	} else {
95137da2899SCharles.Forsyth 		winw = tk->act.width - tk->ipad.x;
95237da2899SCharles.Forsyth 		if(winw <= 0)
95337da2899SCharles.Forsyth 			winw = tk->req.width;
95437da2899SCharles.Forsyth 	}
95537da2899SCharles.Forsyth 	if(winw < 0)
95637da2899SCharles.Forsyth 		return nil;
95737da2899SCharles.Forsyth 
95837da2899SCharles.Forsyth 	/*
95937da2899SCharles.Forsyth 	 * Make lafter be the first line after l2 that comes after a newline
96037da2899SCharles.Forsyth 	 * (so that wrap correction cannot affect it)
96137da2899SCharles.Forsyth 	 */
96237da2899SCharles.Forsyth 	lafter = l2->next;
96337da2899SCharles.Forsyth 	if(tktdbg && lafter == nil) {
96437da2899SCharles.Forsyth 		print("tktfixgeom: botch 1\n");
96537da2899SCharles.Forsyth 		return nil;
96637da2899SCharles.Forsyth 	}
96737da2899SCharles.Forsyth 	while((lafter->flags & TkTfirst) == 0 && lafter != &tkt->end)
96837da2899SCharles.Forsyth 		lafter = lafter->next;
96937da2899SCharles.Forsyth 
97037da2899SCharles.Forsyth 
97137da2899SCharles.Forsyth 	y = l1->orig.y + l1->height + tktpostspace(tk, l1);
97237da2899SCharles.Forsyth 
97337da2899SCharles.Forsyth 	oldi.lo = y;
97437da2899SCharles.Forsyth 	oldi.hi = lafter->orig.y;
97537da2899SCharles.Forsyth 	rest.lo = oldi.hi;
97637da2899SCharles.Forsyth 	rest.hi = rest.lo + 1000; /* get background after end, too */
97737da2899SCharles.Forsyth 
97837da2899SCharles.Forsyth 	opts = mallocz(TkTnumopts*sizeof(int), 0);
97937da2899SCharles.Forsyth 	if(opts == nil)
98037da2899SCharles.Forsyth 		return TkNomem;
98137da2899SCharles.Forsyth 	env = mallocz(sizeof(TkEnv), 0);
98237da2899SCharles.Forsyth 	if(env == nil) {
98337da2899SCharles.Forsyth 		free(opts);
98437da2899SCharles.Forsyth 		return TkNomem;
98537da2899SCharles.Forsyth 	}
98637da2899SCharles.Forsyth 
98737da2899SCharles.Forsyth 	for(l = l1->next; l != lafter; l = l->next) {
98837da2899SCharles.Forsyth 		if(tktdbg && l == nil) {
98937da2899SCharles.Forsyth 			print("tktfixgeom: botch 2\n");
99037da2899SCharles.Forsyth 			free(opts);
99137da2899SCharles.Forsyth 			free(env);
99237da2899SCharles.Forsyth 			return nil;
99337da2899SCharles.Forsyth 		}
99437da2899SCharles.Forsyth 
99537da2899SCharles.Forsyth 		l->flags &= ~TkTdrawn;
99637da2899SCharles.Forsyth 
99737da2899SCharles.Forsyth 		/* some spacing depends on tags of first non-mark on display line */
99837da2899SCharles.Forsyth 		iprev = nil;
99937da2899SCharles.Forsyth 		for(i = l->items; i->kind == TkTmark; ) {
100037da2899SCharles.Forsyth 			iprev = i;
100137da2899SCharles.Forsyth 			i = i->next;
100237da2899SCharles.Forsyth 		}
100337da2899SCharles.Forsyth 		tkttagopts(tk, i, opts, env, &tb, 1);
100437da2899SCharles.Forsyth 
100537da2899SCharles.Forsyth 		if(l->flags&TkTfirst) {
100637da2899SCharles.Forsyth 			xleft = opts[TkTlmargin1];
100737da2899SCharles.Forsyth 			y += opts[TkTspacing1];
100837da2899SCharles.Forsyth 		}
100937da2899SCharles.Forsyth 		else {
101037da2899SCharles.Forsyth 			xleft = opts[TkTlmargin2];
101137da2899SCharles.Forsyth 			y += opts[TkTspacing2];
101237da2899SCharles.Forsyth 		}
101337da2899SCharles.Forsyth 		sp3 = opts[TkTspacing3];
101437da2899SCharles.Forsyth 		just = opts[TkTjustify];
101537da2899SCharles.Forsyth 
101637da2899SCharles.Forsyth 		wrapmode = opts[TkTwrap];
101737da2899SCharles.Forsyth 		f = env->font;
101837da2899SCharles.Forsyth 		h = f->height;
101937da2899SCharles.Forsyth 		lh = opts[TkTlineheight];
102037da2899SCharles.Forsyth 		a = f->ascent;
102137da2899SCharles.Forsyth 		x = xleft;
102237da2899SCharles.Forsyth 		xright = winw - opts[TkTrmargin];
102337da2899SCharles.Forsyth 		if(xright < xleft)
102437da2899SCharles.Forsyth 			xright = xleft;
102537da2899SCharles.Forsyth 
102637da2899SCharles.Forsyth 		/*
102737da2899SCharles.Forsyth 		 * perform line wrapping and calculate h (height) and a (ascent)
102837da2899SCharles.Forsyth 		 * for the current line
102937da2899SCharles.Forsyth 		 */
103037da2899SCharles.Forsyth 		for(; i != nil; iprev = i, i = i->next) {
103137da2899SCharles.Forsyth 		    again:
103237da2899SCharles.Forsyth 			if(i->kind == TkTmark)
103337da2899SCharles.Forsyth 				continue;
103437da2899SCharles.Forsyth 			if(i->kind == TkTnewline)
103537da2899SCharles.Forsyth 				break;
103637da2899SCharles.Forsyth 			if(i->kind == TkTcontline) {
103737da2899SCharles.Forsyth 				/*
103837da2899SCharles.Forsyth 				 * See if some of following line fits on this one.
103937da2899SCharles.Forsyth 				 * First, ensure that following line isn't empty.
104037da2899SCharles.Forsyth 				 */
104137da2899SCharles.Forsyth 				it = l->next->items;
104237da2899SCharles.Forsyth 				while(it->kind == TkTmark)
104337da2899SCharles.Forsyth 					it = it->next;
104437da2899SCharles.Forsyth 
104537da2899SCharles.Forsyth 				if(it->kind == TkTnewline || it->kind == TkTcontline) {
104637da2899SCharles.Forsyth 					/* next line is empty; join it to this one by removing i */
104737da2899SCharles.Forsyth 					ix.item = i;
104837da2899SCharles.Forsyth 					ix.line = l;
104937da2899SCharles.Forsyth 					ix.pos = 0;
105037da2899SCharles.Forsyth 					tktremitem(tkt, &ix);
105137da2899SCharles.Forsyth 					it = l->next->items;
105237da2899SCharles.Forsyth 					if(iprev == nil)
105337da2899SCharles.Forsyth 						i = l->items;
105437da2899SCharles.Forsyth 					else
105537da2899SCharles.Forsyth 						i = iprev->next;
105637da2899SCharles.Forsyth 					goto again;
105737da2899SCharles.Forsyth 				}
105837da2899SCharles.Forsyth 
105937da2899SCharles.Forsyth 				n = xright - x;
106037da2899SCharles.Forsyth 				if(n <= 0)
106137da2899SCharles.Forsyth 					break;
106237da2899SCharles.Forsyth 				ixprev.line = l;
106337da2899SCharles.Forsyth 				ixprev.item = i;
106437da2899SCharles.Forsyth 				ixprev.pos = 0;
106537da2899SCharles.Forsyth 				ix = ixprev;
106637da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbychar, &ix);
106737da2899SCharles.Forsyth 				if(wrapmode == Tkwrapword)
106837da2899SCharles.Forsyth 					tktadjustind(tkt, TkTbywrapend, &ix);
106937da2899SCharles.Forsyth 				if(wrapmode != Tkwrapnone && tktwidbetween(tk, x, &ixprev, &ix) > n)
107037da2899SCharles.Forsyth 					break;
107137da2899SCharles.Forsyth 				/* move one item up from next line and try again */
107237da2899SCharles.Forsyth 				it = l->next->items;
107337da2899SCharles.Forsyth 				if(tktdbg && (it == nil || it->kind == TkTnewline || it->kind == TkTcontline)) {
107437da2899SCharles.Forsyth 					print("tktfixgeom: botch 3\n");
107537da2899SCharles.Forsyth 					free(opts);
107637da2899SCharles.Forsyth 					free(env);
107737da2899SCharles.Forsyth 					return nil;
107837da2899SCharles.Forsyth 				}
107937da2899SCharles.Forsyth 				if(iprev == nil)
108037da2899SCharles.Forsyth 					l->items = it;
108137da2899SCharles.Forsyth 				else
108237da2899SCharles.Forsyth 					iprev->next = it;
108337da2899SCharles.Forsyth 				l->next->items = it->next;
108437da2899SCharles.Forsyth 				it->next = i;
108537da2899SCharles.Forsyth 				i = it;
108637da2899SCharles.Forsyth 				goto again;
108737da2899SCharles.Forsyth 			}
108837da2899SCharles.Forsyth 
108937da2899SCharles.Forsyth 			oa = a;
109037da2899SCharles.Forsyth 			oh = h;
109137da2899SCharles.Forsyth 			if(!tktanytags(i)) {
109237da2899SCharles.Forsyth 				env->font = tk->env->font;
109337da2899SCharles.Forsyth 				o = 0;
109437da2899SCharles.Forsyth 			}
109537da2899SCharles.Forsyth 			else {
109637da2899SCharles.Forsyth 				tkttagopts(tk, i, opts, env, nil, 1);
109737da2899SCharles.Forsyth 				o = opts[TkToffset];
109837da2899SCharles.Forsyth 			}
109937da2899SCharles.Forsyth 			if((o != 0 || env->font != f) && i->kind != TkTwin) {
110037da2899SCharles.Forsyth 				/* check ascent of current item */
110137da2899SCharles.Forsyth 				n = o+env->font->ascent;
110237da2899SCharles.Forsyth 				if(n > a) {
110337da2899SCharles.Forsyth 					a = n;
110437da2899SCharles.Forsyth 					h += (a - oa);
110537da2899SCharles.Forsyth 				}
110637da2899SCharles.Forsyth 				/* check descent of current item */
110737da2899SCharles.Forsyth 				n = (env->font->height - env->font->ascent) - o;
110837da2899SCharles.Forsyth 				if(n > h-a)
110937da2899SCharles.Forsyth 					h = a + n;
111037da2899SCharles.Forsyth 			}
111137da2899SCharles.Forsyth 			if(i->kind == TkTwin && i->iwin->sub != nil) {
111237da2899SCharles.Forsyth 				sub = i->iwin->sub;
111337da2899SCharles.Forsyth 				n = 2 * i->iwin->pady + sub->act.height +
111437da2899SCharles.Forsyth 					2 * sub->borderwidth;
111537da2899SCharles.Forsyth 				switch(i->iwin->align) {
111637da2899SCharles.Forsyth 				case Tktop:
111737da2899SCharles.Forsyth 				case Tkbottom:
111837da2899SCharles.Forsyth 					if(n > h)
111937da2899SCharles.Forsyth 						h = n;
112037da2899SCharles.Forsyth 					break;
112137da2899SCharles.Forsyth 				case Tkcenter:
112237da2899SCharles.Forsyth 					if(n/2 > a)
112337da2899SCharles.Forsyth 						a = n/2;
112437da2899SCharles.Forsyth 					if(n/2 > h-a)
112537da2899SCharles.Forsyth 						h = a + n/2;
112637da2899SCharles.Forsyth 					break;
112737da2899SCharles.Forsyth 				case Tkbaseline:
112837da2899SCharles.Forsyth 					wa = i->iwin->ascent;
112937da2899SCharles.Forsyth 					if (wa == -1)
113037da2899SCharles.Forsyth 						wa = n;
113137da2899SCharles.Forsyth 					h = maximum(a, wa) + maximum(h - a, n - wa);
113237da2899SCharles.Forsyth 					a = maximum(a, wa);
113337da2899SCharles.Forsyth 					break;
113437da2899SCharles.Forsyth 				}
113537da2899SCharles.Forsyth 			}
113637da2899SCharles.Forsyth 
113737da2899SCharles.Forsyth 			w = tktdispwidth(tk, tb, i, env->font, x, 0, -1);
113837da2899SCharles.Forsyth 			n = x + w - xright;
113937da2899SCharles.Forsyth 			if(n > 0 && wrapmode != Tkwrapnone) {
114037da2899SCharles.Forsyth 				/* find shortest suffix that can be removed to fit item */
114137da2899SCharles.Forsyth 				j = tktposcount(i) - 1;
114237da2899SCharles.Forsyth 				while(j > 0 && tktdispwidth(tk, tb, i, env->font, x, j, -1) < n)
114337da2899SCharles.Forsyth 					j--;
114437da2899SCharles.Forsyth 				/* put at least one item on a line before splitting */
114537da2899SCharles.Forsyth 				if(j == 0 && x == xleft) {
114637da2899SCharles.Forsyth 					if(tktposcount(i) == 1)
114737da2899SCharles.Forsyth 						goto Nosplit;
114837da2899SCharles.Forsyth 					j = 1;
114937da2899SCharles.Forsyth 				}
115037da2899SCharles.Forsyth 				ix.line = l;
115137da2899SCharles.Forsyth 				ix.item = i;
115237da2899SCharles.Forsyth 				ix.pos = j;
115337da2899SCharles.Forsyth 				if(wrapmode == Tkwrapword) {
115437da2899SCharles.Forsyth 					/* trim the item at the first word at or before the shortest suffix */
115537da2899SCharles.Forsyth 					/* TO DO: convert any resulting trailing white space to zero width */
115637da2899SCharles.Forsyth 					ixw = ix;
115737da2899SCharles.Forsyth 					if(tktisbreak(tktindrune(&ixw))) {
115837da2899SCharles.Forsyth 						/* at break character, find end of word preceding it */
115937da2899SCharles.Forsyth 						while(tktisbreak(tktindrune(&ixw))){
116037da2899SCharles.Forsyth 							if(!tktadjustind(tkt, TkTbycharback, &ixw) ||
116137da2899SCharles.Forsyth 							   ixw.line != l || ixw.item == l->items && ixw.pos == 0)
116237da2899SCharles.Forsyth 								goto Wrapchar;		/* no suitable point, degrade to char wrap */
116337da2899SCharles.Forsyth 						}
116437da2899SCharles.Forsyth 						ix = ixw;
116537da2899SCharles.Forsyth 					}
116637da2899SCharles.Forsyth 					/* now find start of word */
116737da2899SCharles.Forsyth 					tktadjustind(tkt, TkTbywrapstart, &ixw);
116837da2899SCharles.Forsyth 					if(ixw.line == l && (ixw.item != l->items || ixw.pos > 0)){
116937da2899SCharles.Forsyth 						/* it will leave something on the line, so reasonable to split here */
117037da2899SCharles.Forsyth 						ix = ixw;
117137da2899SCharles.Forsyth 					}
117237da2899SCharles.Forsyth 					/* otherwise degrade to char wrap */
117337da2899SCharles.Forsyth 				}
117437da2899SCharles.Forsyth 			   Wrapchar:
117537da2899SCharles.Forsyth 				if(ix.pos > 0) {
117637da2899SCharles.Forsyth 					needsplit = 1;
117737da2899SCharles.Forsyth 					e = tktsplititem(&ix);
117837da2899SCharles.Forsyth 					if(e != nil) {
117937da2899SCharles.Forsyth 						free(opts);
118037da2899SCharles.Forsyth 						free(env);
118137da2899SCharles.Forsyth 						return e;
118237da2899SCharles.Forsyth 					}
118337da2899SCharles.Forsyth 				}
118437da2899SCharles.Forsyth 				else
118537da2899SCharles.Forsyth 					needsplit = 0;
118637da2899SCharles.Forsyth 
118737da2899SCharles.Forsyth 				e = tktnewitem(TkTcontline, 0, &it);
118837da2899SCharles.Forsyth 				if(e != nil) {
118937da2899SCharles.Forsyth 					free(opts);
119037da2899SCharles.Forsyth 					free(env);
119137da2899SCharles.Forsyth 					return e;
119237da2899SCharles.Forsyth 				}
119337da2899SCharles.Forsyth 				e = tktiteminsert(tkt, &ix, it);
119437da2899SCharles.Forsyth 				if(e != nil) {
119537da2899SCharles.Forsyth 					tktfreeitems(tkt, it, 1);
119637da2899SCharles.Forsyth 					free(opts);
119737da2899SCharles.Forsyth 					free(env);
119837da2899SCharles.Forsyth 					return e;
119937da2899SCharles.Forsyth 				}
120037da2899SCharles.Forsyth 
120137da2899SCharles.Forsyth 				l = l->prev;	/* work on part of line up to split */
120237da2899SCharles.Forsyth 
120337da2899SCharles.Forsyth 				if(needsplit) {
120437da2899SCharles.Forsyth 					/* have to calculate width of pre-split part */
120537da2899SCharles.Forsyth 					ixprev = ix;
120637da2899SCharles.Forsyth 					if(tktadjustind(tkt, TkTbyitemback, &ixprev) &&
120737da2899SCharles.Forsyth 					   tktadjustind(tkt, TkTbyitemback, &ixprev)) {
120837da2899SCharles.Forsyth 						w = tktdispwidth(tk, tb, ixprev.item, nil, x, 0, -1);
120937da2899SCharles.Forsyth 						ixprev.item->width = w;
121037da2899SCharles.Forsyth 						x += w;
121137da2899SCharles.Forsyth 					}
121237da2899SCharles.Forsyth 				}
121337da2899SCharles.Forsyth 				else {
121437da2899SCharles.Forsyth 					h = oh;
121537da2899SCharles.Forsyth 					a = oa;
121637da2899SCharles.Forsyth 				}
121737da2899SCharles.Forsyth 				break;
121837da2899SCharles.Forsyth 			}
121937da2899SCharles.Forsyth 			else {
122037da2899SCharles.Forsyth 			    Nosplit:
122137da2899SCharles.Forsyth 				i->width =w;
122237da2899SCharles.Forsyth 				x += w;
122337da2899SCharles.Forsyth 			}
122437da2899SCharles.Forsyth 		}
122537da2899SCharles.Forsyth 		if (a > h)
122637da2899SCharles.Forsyth 			h = a;
122737da2899SCharles.Forsyth 		if (lh == 0)
122837da2899SCharles.Forsyth 			lh = f->height;
122937da2899SCharles.Forsyth 		if (lh > h) {
123037da2899SCharles.Forsyth 			a += (lh - h) / 2;
123137da2899SCharles.Forsyth 			h = lh;
123237da2899SCharles.Forsyth 		}
123337da2899SCharles.Forsyth 
123437da2899SCharles.Forsyth 		/*
123537da2899SCharles.Forsyth 		 * Now line l is broken correctly and has correct item widths/line height/ascent.
123637da2899SCharles.Forsyth 		 * Merge adjacent TkTascii/TkTrune items with same tags.
123737da2899SCharles.Forsyth 		 * Also, set act{x,y} of embedded widgets to offset from
123837da2899SCharles.Forsyth 		 * left of item box at baseline.
123937da2899SCharles.Forsyth 		 */
124037da2899SCharles.Forsyth 		for(i = l->items; i->next != nil; i = i->next) {
124137da2899SCharles.Forsyth 			it = i->next;
124237da2899SCharles.Forsyth 			if( (i->kind == TkTascii || i->kind == TkTrune)
124337da2899SCharles.Forsyth 			      &&
124437da2899SCharles.Forsyth 			     i->kind == it->kind
124537da2899SCharles.Forsyth 			      &&
124637da2899SCharles.Forsyth 			     tktsametags(i, it)) {
124737da2899SCharles.Forsyth 				n = strlen(i->istring);
124837da2899SCharles.Forsyth 				j = strlen(it->istring);
124937da2899SCharles.Forsyth 				s = realloc(i->istring, n + j + 1);
125037da2899SCharles.Forsyth 				if(s == nil) {
125137da2899SCharles.Forsyth 					free(opts);
125237da2899SCharles.Forsyth 					free(env);
125337da2899SCharles.Forsyth 					return TkNomem;
125437da2899SCharles.Forsyth 				}
125537da2899SCharles.Forsyth 				i->istring = s;
125637da2899SCharles.Forsyth 				memmove(i->istring+n, it->istring, j+1);
125737da2899SCharles.Forsyth 				i->width += it->width;
125837da2899SCharles.Forsyth 				i->next = it->next;
125937da2899SCharles.Forsyth 				it->next = nil;
126037da2899SCharles.Forsyth 				tktfreeitems(tkt, it, 1);
126137da2899SCharles.Forsyth 			}
126237da2899SCharles.Forsyth 			else if(i->kind == TkTwin && i->iwin->sub != nil) {
126337da2899SCharles.Forsyth 				sub = i->iwin->sub;
126437da2899SCharles.Forsyth 				n = sub->act.height + 2 * sub->borderwidth;
126537da2899SCharles.Forsyth 				o = i->iwin->pady;
126637da2899SCharles.Forsyth 				sub->act.x = i->iwin->padx;
126737da2899SCharles.Forsyth 				/*
126837da2899SCharles.Forsyth 				 * sub->act.y is y-origin of widget relative to baseline.
126937da2899SCharles.Forsyth 				 */
127037da2899SCharles.Forsyth 				switch(i->iwin->align) {
127137da2899SCharles.Forsyth 				case Tktop:
127237da2899SCharles.Forsyth 					sub->act.y = o - a;
127337da2899SCharles.Forsyth 					break;
127437da2899SCharles.Forsyth 				case Tkbottom:
127537da2899SCharles.Forsyth 					sub->act.y = h - (o + n) - a;
127637da2899SCharles.Forsyth 					break;
127737da2899SCharles.Forsyth 				case Tkcenter:
127837da2899SCharles.Forsyth 					sub->act.y = (h - n) / 2 - a;
127937da2899SCharles.Forsyth 					break;
128037da2899SCharles.Forsyth 				case Tkbaseline:
128137da2899SCharles.Forsyth 					wa = i->iwin->ascent;
128237da2899SCharles.Forsyth 					if (wa == -1)
128337da2899SCharles.Forsyth 						wa = n;
128437da2899SCharles.Forsyth 					sub->act.y = -wa;
128537da2899SCharles.Forsyth 					break;
128637da2899SCharles.Forsyth 				}
128737da2899SCharles.Forsyth 			}
128837da2899SCharles.Forsyth 		}
128937da2899SCharles.Forsyth 
129037da2899SCharles.Forsyth 		l->width = x - xleft;
129137da2899SCharles.Forsyth 
129237da2899SCharles.Forsyth 		/* justification bug: wrong if line has tabs */
129337da2899SCharles.Forsyth 		l->orig.x = xleft;
129437da2899SCharles.Forsyth 		n = xright - x;
129537da2899SCharles.Forsyth 		if(n > 0) {
129637da2899SCharles.Forsyth 			if(just == Tkright)
129737da2899SCharles.Forsyth 				l->orig.x += n;
129837da2899SCharles.Forsyth 			else
129937da2899SCharles.Forsyth 			if(just == Tkcenter)
130037da2899SCharles.Forsyth 				l->orig.x += n/2;
130137da2899SCharles.Forsyth 		}
130237da2899SCharles.Forsyth 
130337da2899SCharles.Forsyth 		/* give newline or contline width up to right margin */
130437da2899SCharles.Forsyth 		ilast = tktlastitem(l->items);
130537da2899SCharles.Forsyth 		ilast->width = xright - l->width;
130637da2899SCharles.Forsyth 		if(ilast->width < 0)
130737da2899SCharles.Forsyth 			ilast->width = 0;
130837da2899SCharles.Forsyth 
130937da2899SCharles.Forsyth 		l->orig.y = y;
131037da2899SCharles.Forsyth 		l->height = h;
131137da2899SCharles.Forsyth 		l->ascent = a;
131237da2899SCharles.Forsyth 		y += h;
131337da2899SCharles.Forsyth 		if(l->flags&TkTlast)
131437da2899SCharles.Forsyth 			y += sp3;
131537da2899SCharles.Forsyth 	}
131637da2899SCharles.Forsyth 	free(opts);
131737da2899SCharles.Forsyth 	free(env);
131837da2899SCharles.Forsyth 
131937da2899SCharles.Forsyth 	tktdrawbg(tk, oldi.lo, oldi.hi, 0);
132037da2899SCharles.Forsyth 
132137da2899SCharles.Forsyth 	y += tktprespace(tk, l);
132237da2899SCharles.Forsyth 	newrest.lo = y;
132337da2899SCharles.Forsyth 	newrest.hi = y + rest.hi - rest.lo;
132437da2899SCharles.Forsyth 
132537da2899SCharles.Forsyth 	hole = tkttranslate(tk, newrest, rest.lo);
132637da2899SCharles.Forsyth 
132737da2899SCharles.Forsyth 	tktdrawbg(tk, hole.lo, hole.hi, 0);
132837da2899SCharles.Forsyth 
132937da2899SCharles.Forsyth 	if(l != &tkt->end) {
133037da2899SCharles.Forsyth 		while(l != &tkt->end) {
133137da2899SCharles.Forsyth 			oh = l->next->orig.y - l->orig.y;
133237da2899SCharles.Forsyth 			l->orig.y = y;
133337da2899SCharles.Forsyth 			if(y + oh > hole.lo && y < hole.hi) {
133437da2899SCharles.Forsyth 				l->flags &= ~TkTdrawn;
133537da2899SCharles.Forsyth 			}
133637da2899SCharles.Forsyth 			y += oh;
133737da2899SCharles.Forsyth 			l = l->next;
133837da2899SCharles.Forsyth 		}
133937da2899SCharles.Forsyth 	}
134037da2899SCharles.Forsyth 	tkt->end.orig.y = tkt->end.prev->orig.y + tkt->end.prev->height;
134137da2899SCharles.Forsyth 
134237da2899SCharles.Forsyth 	if(tkt->deltatv.y > tkt->end.orig.y)
134337da2899SCharles.Forsyth 		tkt->deltatv.y = tkt->end.prev->orig.y;
134437da2899SCharles.Forsyth 
134537da2899SCharles.Forsyth 
134637da2899SCharles.Forsyth 	e = tktsetscroll(tk, Tkvertical);
134737da2899SCharles.Forsyth 	if(e != nil)
134837da2899SCharles.Forsyth 		return e;
134937da2899SCharles.Forsyth 	e = tktsetscroll(tk, Tkhorizontal);
135037da2899SCharles.Forsyth 	if(e != nil)
135137da2899SCharles.Forsyth 		return e;
135237da2899SCharles.Forsyth 
135337da2899SCharles.Forsyth 	tk->dirty = tkrect(tk, 1);
135437da2899SCharles.Forsyth 	if(tktdbg)
135537da2899SCharles.Forsyth 		tktcheck(tkt, "tktfixgeom end");
135637da2899SCharles.Forsyth 	return nil;
135737da2899SCharles.Forsyth }
135837da2899SCharles.Forsyth 
135937da2899SCharles.Forsyth static int
tktpostspace(Tk * tk,TkTline * l)136037da2899SCharles.Forsyth tktpostspace(Tk *tk, TkTline *l)
136137da2899SCharles.Forsyth {
136237da2899SCharles.Forsyth 	int ans;
136337da2899SCharles.Forsyth 	TkTitem *i;
136437da2899SCharles.Forsyth 	TkEnv env;
136537da2899SCharles.Forsyth 	int *opts;
136637da2899SCharles.Forsyth 
136737da2899SCharles.Forsyth 	opts = mallocz(TkTnumopts*sizeof(int), 0);
136837da2899SCharles.Forsyth 	if(opts == nil)
136937da2899SCharles.Forsyth 		return 0;
137037da2899SCharles.Forsyth 	ans = 0;
137137da2899SCharles.Forsyth 	if(l->items != nil && (l->flags&TkTlast)) {
137237da2899SCharles.Forsyth 		for(i = l->items; i->kind == TkTmark; )
137337da2899SCharles.Forsyth 			i = i->next;
137437da2899SCharles.Forsyth 		tkttagopts(tk, i, opts, &env, nil, 1);
137537da2899SCharles.Forsyth 		ans = opts[TkTspacing3];
137637da2899SCharles.Forsyth 	}
137737da2899SCharles.Forsyth 	free(opts);
137837da2899SCharles.Forsyth 	return ans;
137937da2899SCharles.Forsyth }
138037da2899SCharles.Forsyth 
138137da2899SCharles.Forsyth static int
tktprespace(Tk * tk,TkTline * l)138237da2899SCharles.Forsyth tktprespace(Tk *tk, TkTline *l)
138337da2899SCharles.Forsyth {
138437da2899SCharles.Forsyth 	int ans;
138537da2899SCharles.Forsyth 	TkTitem *i;
138637da2899SCharles.Forsyth 	TkEnv env;
138737da2899SCharles.Forsyth 	int *opts;
138837da2899SCharles.Forsyth 
138937da2899SCharles.Forsyth 	opts = mallocz(TkTnumopts*sizeof(int), 0);
139037da2899SCharles.Forsyth 	if(opts == nil)
139137da2899SCharles.Forsyth 		return 0;
139237da2899SCharles.Forsyth 
139337da2899SCharles.Forsyth 	ans = 0;
139437da2899SCharles.Forsyth 	if(l->items != nil) {
139537da2899SCharles.Forsyth 		for(i = l->items; i->kind == TkTmark; )
139637da2899SCharles.Forsyth 			i = i->next;
139737da2899SCharles.Forsyth 		tkttagopts(tk, i, opts, &env, nil, 1);
139837da2899SCharles.Forsyth 		if(l->flags&TkTfirst)
139937da2899SCharles.Forsyth 			ans = opts[TkTspacing1];
140037da2899SCharles.Forsyth 		else
140137da2899SCharles.Forsyth 			ans = opts[TkTspacing2];
140237da2899SCharles.Forsyth 	}
140337da2899SCharles.Forsyth 	free(opts);
140437da2899SCharles.Forsyth 	return ans;
140537da2899SCharles.Forsyth }
140637da2899SCharles.Forsyth 
140737da2899SCharles.Forsyth static int
tktwidbetween(Tk * tk,int x,TkTindex * i1,TkTindex * i2)140837da2899SCharles.Forsyth tktwidbetween(Tk *tk, int x, TkTindex *i1, TkTindex *i2)
140937da2899SCharles.Forsyth {
141037da2899SCharles.Forsyth 	int d, w, n;
141137da2899SCharles.Forsyth 	TkTindex ix;
141237da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
141337da2899SCharles.Forsyth 
141437da2899SCharles.Forsyth 	w = 0;
141537da2899SCharles.Forsyth 	ix = *i1;
141637da2899SCharles.Forsyth 	while(ix.item != i2->item) {
141737da2899SCharles.Forsyth 		/* probably wrong w.r.t tag tabs */
141837da2899SCharles.Forsyth 		d = tktdispwidth(tk, nil, ix.item, nil, x, ix.pos, -1);
141937da2899SCharles.Forsyth 		w += d;
142037da2899SCharles.Forsyth 		x += d;
142137da2899SCharles.Forsyth 		if(!tktadjustind(tkt, TkTbyitem, &ix)) {
142237da2899SCharles.Forsyth 			if(tktdbg)
142337da2899SCharles.Forsyth 				print("tktwidbetween botch\n");
142437da2899SCharles.Forsyth 			break;
142537da2899SCharles.Forsyth 		}
142637da2899SCharles.Forsyth 	}
142737da2899SCharles.Forsyth 	n = i2->pos - ix.pos;
142837da2899SCharles.Forsyth 	if(n > 0)
142937da2899SCharles.Forsyth 		/* probably wrong w.r.t tag tabs */
143037da2899SCharles.Forsyth 		w += tktdispwidth(tk, nil, ix.item, nil, x, ix.pos, i2->pos-ix.pos);
143137da2899SCharles.Forsyth 	return w;
143237da2899SCharles.Forsyth }
143337da2899SCharles.Forsyth 
143437da2899SCharles.Forsyth static Interval
tktvclip(Interval i,int vh)143537da2899SCharles.Forsyth tktvclip(Interval i, int vh)
143637da2899SCharles.Forsyth {
143737da2899SCharles.Forsyth 	if(i.lo < 0)
143837da2899SCharles.Forsyth 		i.lo = 0;
143937da2899SCharles.Forsyth 	if(i.hi > vh)
144037da2899SCharles.Forsyth 		i.hi = vh;
144137da2899SCharles.Forsyth 	return i;
144237da2899SCharles.Forsyth }
144337da2899SCharles.Forsyth 
144437da2899SCharles.Forsyth /*
144537da2899SCharles.Forsyth  * Do translation of any part of interval that appears on screen
144637da2899SCharles.Forsyth  * starting at srcy to its new position, dsti.
144737da2899SCharles.Forsyth  * Return y-range of the hole left in the image (either because
144837da2899SCharles.Forsyth  * the src bits were out of the V window, or because the src bits
144937da2899SCharles.Forsyth  * vacated an area of the V window).
145037da2899SCharles.Forsyth  * The coordinates passed in and out are in T space.
145137da2899SCharles.Forsyth  */
145237da2899SCharles.Forsyth static Interval
tkttranslate(Tk * tk,Interval dsti,int srcy)145337da2899SCharles.Forsyth tkttranslate(Tk *tk, Interval dsti, int srcy)
145437da2899SCharles.Forsyth {
145537da2899SCharles.Forsyth 	int vh, vw, dvty, locked;
145637da2899SCharles.Forsyth 	TkText *tkt;
145737da2899SCharles.Forsyth 	Image *i;
145837da2899SCharles.Forsyth 	Interval hole, vdst, vsrc;
145937da2899SCharles.Forsyth 	Point src;
146037da2899SCharles.Forsyth 	Rectangle dst;
146137da2899SCharles.Forsyth 	Display *d;
146237da2899SCharles.Forsyth 
146337da2899SCharles.Forsyth 	hole.hi = 0;
146437da2899SCharles.Forsyth 	hole.lo = 0;
146537da2899SCharles.Forsyth 
146637da2899SCharles.Forsyth 
146737da2899SCharles.Forsyth 	/*
146837da2899SCharles.Forsyth 	 * If we are embedded in a text widget, we need to come in through
146937da2899SCharles.Forsyth 	 * the tkdrawtext routine, to ensure our clipr is set properly, so we
147037da2899SCharles.Forsyth 	 * just punt in that case.
147137da2899SCharles.Forsyth 	 * XXX is just checking parent good enough. what if we're in
147237da2899SCharles.Forsyth 	 * a frame in a text widget?
147337da2899SCharles.Forsyth 	 * BUG!
147437da2899SCharles.Forsyth 
147537da2899SCharles.Forsyth 	* if(tk->parent != nil && tk->parent->type == TKtext) {
147637da2899SCharles.Forsyth 	*	tk->flag |= Tkrefresh;
147737da2899SCharles.Forsyth 	*	return hole;
147837da2899SCharles.Forsyth 	* }
147937da2899SCharles.Forsyth 	*/
148037da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
148137da2899SCharles.Forsyth 	dvty = tkt->deltatv.y;
148237da2899SCharles.Forsyth 	i = tkt->image;
148337da2899SCharles.Forsyth 
148437da2899SCharles.Forsyth 	vw = tk->act.width - tk->ipad.x;
148537da2899SCharles.Forsyth 	vh = tk->act.height - tk->ipad.y;
148637da2899SCharles.Forsyth 
148737da2899SCharles.Forsyth 	/* convert to V space */
148837da2899SCharles.Forsyth 	vdst.lo = dsti.lo - dvty;
148937da2899SCharles.Forsyth 	vdst.hi = dsti.hi - dvty;
149037da2899SCharles.Forsyth 	vsrc.lo = srcy - dvty;
149137da2899SCharles.Forsyth 	vsrc.hi = vsrc.lo + dsti.hi - dsti.lo;
149237da2899SCharles.Forsyth 	if(vsrc.lo == vsrc.hi || vsrc.lo == vdst.lo)
149337da2899SCharles.Forsyth 		return hole;
149437da2899SCharles.Forsyth 	else if(vsrc.hi <= 0 || vsrc.lo >= vh)
149537da2899SCharles.Forsyth 		hole = tktvclip(vdst, vh);
149637da2899SCharles.Forsyth 	else if(vdst.hi <= 0 || vdst.lo >= vh)
149737da2899SCharles.Forsyth 		hole = tktvclip(vsrc, vh);
149837da2899SCharles.Forsyth 	else if(i != nil) {
149937da2899SCharles.Forsyth 		src.x = 0;
150037da2899SCharles.Forsyth 		src.y = vsrc.lo;
150137da2899SCharles.Forsyth 		if(vdst.lo > vsrc.lo) {  /* see earlier text lines */
150237da2899SCharles.Forsyth 			if(vsrc.lo < 0) {
150337da2899SCharles.Forsyth 				src.y = 0;
150437da2899SCharles.Forsyth 				vdst.lo -= vsrc.lo;
150537da2899SCharles.Forsyth 			}
150637da2899SCharles.Forsyth 			if(vdst.hi > vh)
150737da2899SCharles.Forsyth 				vdst.hi = vh;
150837da2899SCharles.Forsyth 			hole.lo = src.y;
150937da2899SCharles.Forsyth 			hole.hi = vdst.lo;
151037da2899SCharles.Forsyth 		}
151137da2899SCharles.Forsyth 		else {  /* see later text lines */
151237da2899SCharles.Forsyth 			if(vsrc.hi > vh)
151337da2899SCharles.Forsyth 				vdst.hi -= (vsrc.hi - vh);
151437da2899SCharles.Forsyth 			if(vdst.lo < 0){
151537da2899SCharles.Forsyth 				src.y -= vdst.lo;
151637da2899SCharles.Forsyth 				vdst.lo = 0;
151737da2899SCharles.Forsyth 			}
151837da2899SCharles.Forsyth 			hole.lo = vdst.hi;
151937da2899SCharles.Forsyth 			hole.hi = src.y + (vdst.hi - vdst.lo);
152037da2899SCharles.Forsyth 		}
152137da2899SCharles.Forsyth 		if(vdst.hi > vdst.lo && (tkt->tflag&TkTdrawn)) {
152237da2899SCharles.Forsyth 			src = addpt(src, tkt->deltaiv);
152337da2899SCharles.Forsyth 			dst = rectaddpt(Rect(0, vdst.lo, vw, vdst.hi), tkt->deltaiv);
152437da2899SCharles.Forsyth 			d = tk->env->top->display;
152537da2899SCharles.Forsyth 			locked = 0;
152637da2899SCharles.Forsyth 			if(!(tkt->tflag&TkTdlocked))
152737da2899SCharles.Forsyth 				locked = lockdisplay(d);
152837da2899SCharles.Forsyth 			i = tkimageof(tk);
152937da2899SCharles.Forsyth 			tkt->image = i;
153037da2899SCharles.Forsyth 			if(i != nil)
153137da2899SCharles.Forsyth 				draw(i, dst, i, nil, src);
153237da2899SCharles.Forsyth 			if(locked)
153337da2899SCharles.Forsyth 				unlockdisplay(d);
153437da2899SCharles.Forsyth 		}
153537da2899SCharles.Forsyth 	}
153637da2899SCharles.Forsyth 	hole.lo += dvty;
153737da2899SCharles.Forsyth 	hole.hi += dvty;
153837da2899SCharles.Forsyth 	return hole;
153937da2899SCharles.Forsyth }
154037da2899SCharles.Forsyth 
154137da2899SCharles.Forsyth /*
154237da2899SCharles.Forsyth  * mark lines from firsty to lasty as not drawn.
154337da2899SCharles.Forsyth  * firsty and lasty are in T space
154437da2899SCharles.Forsyth  */
154537da2899SCharles.Forsyth static void
tktnotdrawn(Tk * tk,int firsty,int lasty,int all)154637da2899SCharles.Forsyth tktnotdrawn(Tk *tk, int firsty, int lasty, int all)
154737da2899SCharles.Forsyth {
154837da2899SCharles.Forsyth 	TkTline *lend, *l;
154937da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
155037da2899SCharles.Forsyth 	if(firsty >= lasty && !all)
155137da2899SCharles.Forsyth 		return;
155237da2899SCharles.Forsyth 	lend = &tkt->end;
155337da2899SCharles.Forsyth 	for(l = tkt->start.next; l != lend; l = l->next) {
155437da2899SCharles.Forsyth 		if(l->orig.y+l->height <= firsty)
155537da2899SCharles.Forsyth 			continue;
155637da2899SCharles.Forsyth 		if(l->orig.y >= lasty)
155737da2899SCharles.Forsyth 			break;
155837da2899SCharles.Forsyth 		l->flags &= ~TkTdrawn;
155937da2899SCharles.Forsyth 		if (firsty > l->orig.y)
156037da2899SCharles.Forsyth 			firsty = l->orig.y;
156137da2899SCharles.Forsyth 		if (lasty < l->orig.y+l->height)
156237da2899SCharles.Forsyth 			lasty = l->orig.y+l->height;
156337da2899SCharles.Forsyth 	}
156437da2899SCharles.Forsyth 	tktdrawbg(tk, firsty, lasty, all);
156537da2899SCharles.Forsyth 	tk->dirty = tkrect(tk, 1);
156637da2899SCharles.Forsyth }
156737da2899SCharles.Forsyth 
156837da2899SCharles.Forsyth /*
156937da2899SCharles.Forsyth  * firsty and lasty are in T space
157037da2899SCharles.Forsyth  */
157137da2899SCharles.Forsyth static void
tktdrawbg(Tk * tk,int firsty,int lasty,int all)157237da2899SCharles.Forsyth tktdrawbg(Tk *tk, int firsty, int lasty, int all)
157337da2899SCharles.Forsyth {
157437da2899SCharles.Forsyth 	int vw, vh, locked;
157537da2899SCharles.Forsyth 	Rectangle r;
157637da2899SCharles.Forsyth 	Image *i;
157737da2899SCharles.Forsyth 	Display *d;
157837da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
157937da2899SCharles.Forsyth 
158037da2899SCharles.Forsyth 	if(tk->env->top->root->flag & Tksuspended){
158137da2899SCharles.Forsyth 		tk->flag |= Tkrefresh;
158237da2899SCharles.Forsyth 		return;
158337da2899SCharles.Forsyth 	}
158437da2899SCharles.Forsyth 	/*
158537da2899SCharles.Forsyth 	 * If we are embedded in a text widget, we need to come in through
158637da2899SCharles.Forsyth 	 * the tkdrawtext routine, to ensure our clipr is set properly, so we
158737da2899SCharles.Forsyth 	 * just punt in that case.
158837da2899SCharles.Forsyth 	 * BUG!
158937da2899SCharles.Forsyth 	 * if(tk->parent != nil && tk->parent->type == TKtext) {
159037da2899SCharles.Forsyth 	 * 	tk->flag |= Tkrefresh;
159137da2899SCharles.Forsyth 	 * 	return;
159237da2899SCharles.Forsyth 	 * }
159337da2899SCharles.Forsyth 	 */
159437da2899SCharles.Forsyth 	vw = tk->act.width - tk->ipad.x;
159537da2899SCharles.Forsyth 	vh = tk->act.height - tk->ipad.y;
159637da2899SCharles.Forsyth 	if(all) {
159737da2899SCharles.Forsyth 		/* whole background is to be drawn, not just until last line */
159837da2899SCharles.Forsyth 		firsty = 0;
159937da2899SCharles.Forsyth 		lasty = 100000;
160037da2899SCharles.Forsyth 	}
160137da2899SCharles.Forsyth 	if(firsty >= lasty)
160237da2899SCharles.Forsyth 		return;
160337da2899SCharles.Forsyth 	firsty -= tkt->deltatv.y;
160437da2899SCharles.Forsyth 	lasty -= tkt->deltatv.y;
160537da2899SCharles.Forsyth 	if(firsty < 0)
160637da2899SCharles.Forsyth 		firsty = 0;
160737da2899SCharles.Forsyth 	if(lasty > vh)
160837da2899SCharles.Forsyth 		lasty = vh;
160937da2899SCharles.Forsyth 	r = rectaddpt(Rect(0, firsty, vw, lasty), tkt->deltaiv);
161037da2899SCharles.Forsyth 	if(r.min.y < r.max.y && (tkt->tflag&TkTdrawn)) {
161137da2899SCharles.Forsyth 		d = tk->env->top->display;
161237da2899SCharles.Forsyth 		locked = 0;
161337da2899SCharles.Forsyth 		if(!(tkt->tflag&TkTdlocked))
161437da2899SCharles.Forsyth 			locked = lockdisplay(d);
161537da2899SCharles.Forsyth 		i = tkimageof(tk);
161637da2899SCharles.Forsyth 		tkt->image = i;
161737da2899SCharles.Forsyth 		if(i != nil)
161837da2899SCharles.Forsyth 			draw(i, r, tkgc(tk->env, TkCbackgnd), nil, ZP);
161937da2899SCharles.Forsyth 		if(locked)
162037da2899SCharles.Forsyth 			unlockdisplay(d);
162137da2899SCharles.Forsyth 	}
162237da2899SCharles.Forsyth }
162337da2899SCharles.Forsyth 
162437da2899SCharles.Forsyth static void
tktfixscroll(Tk * tk,Point odeltatv)162537da2899SCharles.Forsyth tktfixscroll(Tk *tk, Point odeltatv)
162637da2899SCharles.Forsyth {
162737da2899SCharles.Forsyth 	int lasty;
162837da2899SCharles.Forsyth 	Interval oi, hole;
162937da2899SCharles.Forsyth 	Rectangle oclipr;
163037da2899SCharles.Forsyth 	Image *dst;
163137da2899SCharles.Forsyth 	Point ndeltatv;
163237da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
163337da2899SCharles.Forsyth 
163437da2899SCharles.Forsyth 	ndeltatv = tkt->deltatv;
163537da2899SCharles.Forsyth 
163637da2899SCharles.Forsyth 	if(eqpt(odeltatv, ndeltatv))
163737da2899SCharles.Forsyth 		return;
163837da2899SCharles.Forsyth 
163937da2899SCharles.Forsyth 	/* set clipr to avoid spilling outside (in case didn't come in through draw) */
164037da2899SCharles.Forsyth 	dst = tkimageof(tk);
164137da2899SCharles.Forsyth 	if(dst != nil) {
164237da2899SCharles.Forsyth 		tkt->image = dst;
164337da2899SCharles.Forsyth 		oclipr = dst->clipr;
164437da2899SCharles.Forsyth 		tktsetclip(tk);
164537da2899SCharles.Forsyth 	}
164637da2899SCharles.Forsyth 
164737da2899SCharles.Forsyth 	lasty = tkt->end.orig.y;
164837da2899SCharles.Forsyth 	if(odeltatv.x != ndeltatv.x)
164937da2899SCharles.Forsyth 		tktnotdrawn(tk, ndeltatv.y, lasty, 0);
165037da2899SCharles.Forsyth 	else {
165137da2899SCharles.Forsyth 		oi.lo = odeltatv.y;
165237da2899SCharles.Forsyth 		oi.hi = lasty;
165337da2899SCharles.Forsyth 		hole = tkttranslate(tk, oi, ndeltatv.y);
165437da2899SCharles.Forsyth 		tktnotdrawn(tk, hole.lo, hole.hi, 0);
165537da2899SCharles.Forsyth 	}
165637da2899SCharles.Forsyth 	if(dst != nil)
165737da2899SCharles.Forsyth 		tktreplclipr(dst, oclipr);
165837da2899SCharles.Forsyth }
165937da2899SCharles.Forsyth 
166037da2899SCharles.Forsyth void
tktextgeom(Tk * tk)166137da2899SCharles.Forsyth tktextgeom(Tk *tk)
166237da2899SCharles.Forsyth {
166337da2899SCharles.Forsyth 	TkTindex ix;
166437da2899SCharles.Forsyth 	Rectangle oclipr;
166537da2899SCharles.Forsyth 	Image *dst;
166637da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
166737da2899SCharles.Forsyth 	char buf[20], *p;
166837da2899SCharles.Forsyth 
166937da2899SCharles.Forsyth 	tkt->tflag &= ~TkTdrawn;
167037da2899SCharles.Forsyth 	tktsetdeltas(tk, ZP);
167137da2899SCharles.Forsyth 	/* find index of current top-left, so can see it again */
167237da2899SCharles.Forsyth 	tktxyind(tk, 0, 0, &ix);
167337da2899SCharles.Forsyth 	/* make sure scroll bar is redrawn */
167437da2899SCharles.Forsyth 	tkt->scrolltop[Tkvertical] = -1;
167537da2899SCharles.Forsyth 	tkt->scrolltop[Tkhorizontal] = -1;
167637da2899SCharles.Forsyth 	tkt->scrollbot[Tkvertical] = -1;
167737da2899SCharles.Forsyth 	tkt->scrollbot[Tkhorizontal] = -1;
167837da2899SCharles.Forsyth 
167937da2899SCharles.Forsyth 	/* set clipr to avoid spilling outside (didn't come in through draw) */
168037da2899SCharles.Forsyth 	dst = tkimageof(tk);
168137da2899SCharles.Forsyth 	if(dst != nil) {
168237da2899SCharles.Forsyth 		tkt->image = dst;
168337da2899SCharles.Forsyth 		oclipr = dst->clipr;
168437da2899SCharles.Forsyth 		tktsetclip(tk);
168537da2899SCharles.Forsyth 	}
168637da2899SCharles.Forsyth 
168737da2899SCharles.Forsyth 	/*
168837da2899SCharles.Forsyth 	 * have to save index in a reusable format, as
168937da2899SCharles.Forsyth 	 * tktfixgeom can free everything that ix points to.
169037da2899SCharles.Forsyth 	 */
169137da2899SCharles.Forsyth 	snprint(buf, sizeof(buf), "%d.%d", tktlinenum(tkt, &ix), tktlinepos(tkt, &ix));
169237da2899SCharles.Forsyth 	tktfixgeom(tk, &tkt->start, tkt->end.prev, 1);
169337da2899SCharles.Forsyth 	p = buf;
169437da2899SCharles.Forsyth 	tktindparse(tk, &p, &ix);		/* restore index to something close to original value */
169537da2899SCharles.Forsyth 	tktsee(tk, &ix, 1);
169637da2899SCharles.Forsyth 
169737da2899SCharles.Forsyth 	if(dst != nil)
169837da2899SCharles.Forsyth 		tktreplclipr(dst, oclipr);
169937da2899SCharles.Forsyth }
170037da2899SCharles.Forsyth 
170137da2899SCharles.Forsyth static char*
tktsetscroll(Tk * tk,int orient)170237da2899SCharles.Forsyth tktsetscroll(Tk *tk, int orient)
170337da2899SCharles.Forsyth {
170437da2899SCharles.Forsyth 	TkText *tkt;
170537da2899SCharles.Forsyth 	TkTline *l;
170637da2899SCharles.Forsyth 	int ntot, nmin, nmax, top, bot, vw, vh;
170737da2899SCharles.Forsyth 	char *val, *cmd, *v, *e, *s;
170837da2899SCharles.Forsyth 
170937da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
171037da2899SCharles.Forsyth 
171137da2899SCharles.Forsyth 	s = (orient == Tkvertical)? tkt->yscroll : tkt->xscroll;
171237da2899SCharles.Forsyth 	if(s == nil)
171337da2899SCharles.Forsyth 		return nil;
171437da2899SCharles.Forsyth 
171537da2899SCharles.Forsyth 	vw = tk->act.width - tk->ipad.x;
171637da2899SCharles.Forsyth 	vh = tk->act.height - tk->ipad.y;
171737da2899SCharles.Forsyth 
171837da2899SCharles.Forsyth 	if(orient == Tkvertical) {
171937da2899SCharles.Forsyth 		l = tkt->end.prev;
172037da2899SCharles.Forsyth 		ntot = l->orig.y + l->height;
172137da2899SCharles.Forsyth 		nmin = tkt->deltatv.y;
172237da2899SCharles.Forsyth 		if(vh <= 0)
172337da2899SCharles.Forsyth 			nmax = nmin;
172437da2899SCharles.Forsyth 		else
172537da2899SCharles.Forsyth 			nmax = nmin + vh;
172637da2899SCharles.Forsyth 	}
172737da2899SCharles.Forsyth 	else {
172837da2899SCharles.Forsyth 		ntot = tktmaxwid(tkt->start.next);
172937da2899SCharles.Forsyth 		nmin = tkt->deltatv.x;
173037da2899SCharles.Forsyth 		if(vw <= 0)
173137da2899SCharles.Forsyth 			nmax = nmin;
173237da2899SCharles.Forsyth 		else
173337da2899SCharles.Forsyth 			nmax = nmin + vw;
173437da2899SCharles.Forsyth 	}
173537da2899SCharles.Forsyth 
173637da2899SCharles.Forsyth 	if(ntot == 0) {
173737da2899SCharles.Forsyth 		top = 0;
173837da2899SCharles.Forsyth 		bot = TKI2F(1);
173937da2899SCharles.Forsyth 	}
174037da2899SCharles.Forsyth 	else {
174137da2899SCharles.Forsyth 		if(ntot < nmax)
174237da2899SCharles.Forsyth 			ntot = nmax;
174337da2899SCharles.Forsyth 		top = TKI2F(nmin)/ntot;
174437da2899SCharles.Forsyth 		bot = TKI2F(nmax)/ntot;
174537da2899SCharles.Forsyth 	}
174637da2899SCharles.Forsyth 
174737da2899SCharles.Forsyth 	if(tkt->scrolltop[orient] == top && tkt->scrollbot[orient] == bot)
174837da2899SCharles.Forsyth 		return nil;
174937da2899SCharles.Forsyth 
175037da2899SCharles.Forsyth 	tkt->scrolltop[orient] = top;
175137da2899SCharles.Forsyth 	tkt->scrollbot[orient] = bot;
175237da2899SCharles.Forsyth 
175337da2899SCharles.Forsyth 	val = mallocz(Tkminitem, 0);
175437da2899SCharles.Forsyth 	if(val == nil)
175537da2899SCharles.Forsyth 		return TkNomem;
175637da2899SCharles.Forsyth 	cmd = mallocz(Tkmaxitem, 0);
175737da2899SCharles.Forsyth 	if(cmd == nil) {
175837da2899SCharles.Forsyth 		free(val);
175937da2899SCharles.Forsyth 		return TkNomem;
176037da2899SCharles.Forsyth 	}
176137da2899SCharles.Forsyth 
176237da2899SCharles.Forsyth 	v = tkfprint(val, top);
176337da2899SCharles.Forsyth 	*v++ = ' ';
176437da2899SCharles.Forsyth 	tkfprint(v, bot);
176537da2899SCharles.Forsyth 	snprint(cmd, Tkmaxitem, "%s %s", s, val);
176637da2899SCharles.Forsyth 	e = tkexec(tk->env->top, cmd, nil);
176737da2899SCharles.Forsyth 	free(cmd);
176837da2899SCharles.Forsyth 	free(val);
176937da2899SCharles.Forsyth 	return e;
177037da2899SCharles.Forsyth }
177137da2899SCharles.Forsyth 
177237da2899SCharles.Forsyth static char*
tktview(Tk * tk,char * arg,char ** val,int nl,int * posn,int max,int orient)177337da2899SCharles.Forsyth tktview(Tk *tk, char *arg, char **val, int nl, int *posn, int max, int orient)
177437da2899SCharles.Forsyth {
177537da2899SCharles.Forsyth 	int top, bot, amount, n;
177637da2899SCharles.Forsyth 	char buf[Tkminitem], *v, *e;
177737da2899SCharles.Forsyth 
177837da2899SCharles.Forsyth 	if(*arg == '\0') {
177937da2899SCharles.Forsyth 		if ( max == 0 ) {
178037da2899SCharles.Forsyth 			top = 0;
178137da2899SCharles.Forsyth 			bot = TKI2F(1);
178237da2899SCharles.Forsyth 		}
178337da2899SCharles.Forsyth 		else {
178437da2899SCharles.Forsyth 			top = TKI2F(*posn)/max;
178537da2899SCharles.Forsyth 			bot = TKI2F(*posn+nl)/max;
178637da2899SCharles.Forsyth 			if (bot > TKI2F(1))
178737da2899SCharles.Forsyth 				bot = TKI2F(1);
178837da2899SCharles.Forsyth 		}
178937da2899SCharles.Forsyth 		v = tkfprint(buf, top);
179037da2899SCharles.Forsyth 		*v++ = ' ';
179137da2899SCharles.Forsyth 		tkfprint(v, bot);
179237da2899SCharles.Forsyth 		return tkvalue(val, "%s", buf);
179337da2899SCharles.Forsyth 	}
179437da2899SCharles.Forsyth 
179537da2899SCharles.Forsyth 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
179637da2899SCharles.Forsyth 	if(strcmp(buf, "moveto") == 0) {
179737da2899SCharles.Forsyth 		e = tkfracword(tk->env->top, &arg, &top, nil);
179837da2899SCharles.Forsyth 		if (e != nil)
179937da2899SCharles.Forsyth 			return e;
180037da2899SCharles.Forsyth 		*posn = TKF2I(top*max);
180137da2899SCharles.Forsyth 	}
180237da2899SCharles.Forsyth 	else
180337da2899SCharles.Forsyth 	if(strcmp(buf, "scroll") == 0) {
18046e425a9dSCharles.Forsyth 		e = tkfracword(tk->env->top, &arg, &amount, nil);
18056e425a9dSCharles.Forsyth 		if(e != nil)
18066e425a9dSCharles.Forsyth 			return e;
180737da2899SCharles.Forsyth 		arg = tkskip(arg, " \t");
180837da2899SCharles.Forsyth 		if(*arg == 'p')		/* Pages */
180937da2899SCharles.Forsyth 			amount *= nl;
181037da2899SCharles.Forsyth 		else				/* Lines or Characters */
181137da2899SCharles.Forsyth 		if(orient == Tkvertical) {
181237da2899SCharles.Forsyth 			/* XXX needs improvement */
181337da2899SCharles.Forsyth 			amount *= tk->env->font->height;
181437da2899SCharles.Forsyth 		}
181537da2899SCharles.Forsyth 		else
181637da2899SCharles.Forsyth 			amount *= tk->env->wzero;
18176e425a9dSCharles.Forsyth 		amount = TKF2I(amount);
181837da2899SCharles.Forsyth 		n = *posn + amount;
181937da2899SCharles.Forsyth 		if(n < 0)
182037da2899SCharles.Forsyth 			n = 0;
182137da2899SCharles.Forsyth 		if(n > max)
182237da2899SCharles.Forsyth 			n = max;
182337da2899SCharles.Forsyth 		*posn = n;
182437da2899SCharles.Forsyth 	}
182537da2899SCharles.Forsyth 	else
182637da2899SCharles.Forsyth 		return TkBadcm;
182737da2899SCharles.Forsyth 
182837da2899SCharles.Forsyth 	bot = max - (nl * 3 / 4);
182937da2899SCharles.Forsyth 	if(*posn > bot)
183037da2899SCharles.Forsyth 		*posn = bot;
183137da2899SCharles.Forsyth 	if(*posn < 0)
183237da2899SCharles.Forsyth 		*posn = 0;
183337da2899SCharles.Forsyth 
183437da2899SCharles.Forsyth 	return nil;
183537da2899SCharles.Forsyth }
183637da2899SCharles.Forsyth 
183737da2899SCharles.Forsyth static void
tktclearsel(Tk * tk)183837da2899SCharles.Forsyth tktclearsel(Tk *tk)
183937da2899SCharles.Forsyth {
184037da2899SCharles.Forsyth 	TkTindex ibeg, iend;
184137da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
184237da2899SCharles.Forsyth 
184337da2899SCharles.Forsyth 	if(tkt->selfirst == nil)
184437da2899SCharles.Forsyth 		return;
184537da2899SCharles.Forsyth 	tktitemind(tkt->selfirst, &ibeg);
184637da2899SCharles.Forsyth 	tktitemind(tkt->sellast, &iend);
184737da2899SCharles.Forsyth 
184837da2899SCharles.Forsyth 	tkttagchange(tk, TkTselid, &ibeg, &iend, 0);
184937da2899SCharles.Forsyth }
185037da2899SCharles.Forsyth 
185137da2899SCharles.Forsyth static int
tktgetsel(Tk * tk,TkTindex * i1,TkTindex * i2)185237da2899SCharles.Forsyth tktgetsel(Tk *tk, TkTindex *i1, TkTindex *i2)
185337da2899SCharles.Forsyth {
185437da2899SCharles.Forsyth 	TkText *tkt =TKobj(TkText, tk);
185537da2899SCharles.Forsyth 
185637da2899SCharles.Forsyth 	if(tkt->selfirst == nil)
185737da2899SCharles.Forsyth 		return 0;
185837da2899SCharles.Forsyth 	tktitemind(tkt->selfirst, i1);
185937da2899SCharles.Forsyth 	tktitemind(tkt->sellast, i2);
186037da2899SCharles.Forsyth 	return 1;
186137da2899SCharles.Forsyth }
186237da2899SCharles.Forsyth 
186337da2899SCharles.Forsyth /*
186437da2899SCharles.Forsyth  * Adjust tkt->deltatv so that indexed character is visible.
186537da2899SCharles.Forsyth  *	- if seetop is true, make indexed char be at top of window
186637da2899SCharles.Forsyth  *	- if it is already visible, do nothing.
186737da2899SCharles.Forsyth  *	- if it is > 1/2 screenful off edge of screen, center it
186837da2899SCharles.Forsyth  *	   else put it at bottom or top (whichever is nearer)
186937da2899SCharles.Forsyth  *	- if first line is visible, put it at top
187037da2899SCharles.Forsyth  *	- if last line is visible, allow one blank line at bottom
187137da2899SCharles.Forsyth  *
187237da2899SCharles.Forsyth  * BUG: should handle x visibility too
187337da2899SCharles.Forsyth  */
187437da2899SCharles.Forsyth static void
tktsee(Tk * tk,TkTindex * ixp,int seetop)187537da2899SCharles.Forsyth tktsee(Tk *tk, TkTindex *ixp, int seetop)
187637da2899SCharles.Forsyth {
187737da2899SCharles.Forsyth 	int ycur, ynext, deltatvy, adjy, h;
187837da2899SCharles.Forsyth 	Point p, odeltatv;
187937da2899SCharles.Forsyth 	Rectangle bbox;
188037da2899SCharles.Forsyth 	TkTline *l, *el;
188137da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
188237da2899SCharles.Forsyth 	TkTindex ix;
188337da2899SCharles.Forsyth 
188437da2899SCharles.Forsyth 	ix = *ixp;
188537da2899SCharles.Forsyth 	deltatvy = tkt->deltatv.y;
188637da2899SCharles.Forsyth 	odeltatv = tkt->deltatv;
188737da2899SCharles.Forsyth 	h = tk->act.height;
188837da2899SCharles.Forsyth 
188937da2899SCharles.Forsyth 	/* find p (in T space): top left of indexed line */
189037da2899SCharles.Forsyth 	l = ix.line;
189137da2899SCharles.Forsyth 	p = l->orig;
189237da2899SCharles.Forsyth 
189337da2899SCharles.Forsyth 	/* ycur, ynext in V space */
189437da2899SCharles.Forsyth 	ycur = p.y - deltatvy;
189537da2899SCharles.Forsyth 	ynext = ycur + l->height;
189637da2899SCharles.Forsyth 	adjy = 0;
189737da2899SCharles.Forsyth 
189837da2899SCharles.Forsyth 	/* quantize h to line boundaries (works if single font) */
189937da2899SCharles.Forsyth 	if ( l->height )
190037da2899SCharles.Forsyth 		h -= h%l->height;
190137da2899SCharles.Forsyth 
190237da2899SCharles.Forsyth 	if(seetop) {
190337da2899SCharles.Forsyth 		deltatvy = p.y;
190437da2899SCharles.Forsyth 		adjy = 1;
190537da2899SCharles.Forsyth 	}
190637da2899SCharles.Forsyth 	else
190737da2899SCharles.Forsyth 	if(ycur < 0 || ynext >= h) {
190837da2899SCharles.Forsyth 		adjy = 1;
190937da2899SCharles.Forsyth 
191037da2899SCharles.Forsyth 		if(ycur < -h/2 || ycur > 3*h/2)
191137da2899SCharles.Forsyth 			deltatvy = p.y - h/2;
191237da2899SCharles.Forsyth 		else if(ycur < 0)
191337da2899SCharles.Forsyth 			deltatvy = p.y;
191437da2899SCharles.Forsyth 		else
191537da2899SCharles.Forsyth 			deltatvy = p.y - h + l->height;
191637da2899SCharles.Forsyth 
191737da2899SCharles.Forsyth 		el = tkt->end.prev;
191837da2899SCharles.Forsyth 		if(el != nil && el->orig.y - deltatvy < h)
191937da2899SCharles.Forsyth 			deltatvy = tkt->end.orig.y - (h * 3 / 4);
192037da2899SCharles.Forsyth 
192137da2899SCharles.Forsyth 		if(p.y - deltatvy < 0)
192237da2899SCharles.Forsyth 			deltatvy = p.y;
192337da2899SCharles.Forsyth 		if(deltatvy < 0)
192437da2899SCharles.Forsyth 			deltatvy = 0;
192537da2899SCharles.Forsyth 	}
192637da2899SCharles.Forsyth 	if(adjy) {
192737da2899SCharles.Forsyth 		tkt->deltatv.y = deltatvy;
192837da2899SCharles.Forsyth 		tktsetscroll(tk, Tkvertical);	/* XXX - Tad: err ignored */
192937da2899SCharles.Forsyth 		tktfixscroll(tk, odeltatv);
193037da2899SCharles.Forsyth 	}
193137da2899SCharles.Forsyth 	while (ix.item->kind == TkTmark)
193237da2899SCharles.Forsyth 		ix.item = ix.item->next;
193337da2899SCharles.Forsyth 	bbox = tktbbox(tk, &ix);
193437da2899SCharles.Forsyth 	/* make sure that cursor at the end gets shown */
193537da2899SCharles.Forsyth 	tksee(tk, bbox, Pt(bbox.min.x, (bbox.min.y + bbox.max.y) / 2));
193637da2899SCharles.Forsyth }
193737da2899SCharles.Forsyth 
193837da2899SCharles.Forsyth static int
tktcmatch(int c1,int c2,int nocase)193937da2899SCharles.Forsyth tktcmatch(int c1, int c2, int nocase)
194037da2899SCharles.Forsyth {
194137da2899SCharles.Forsyth 	if(nocase) {
194237da2899SCharles.Forsyth 		if(c1 >= 'a' && c1 <= 'z')
194337da2899SCharles.Forsyth 			c1 -= 'a' - 'A';
194437da2899SCharles.Forsyth 		if(c2 >= 'a' && c2 <= 'z')
194537da2899SCharles.Forsyth 			c2 -= 'a' - 'A';
194637da2899SCharles.Forsyth 	}
194737da2899SCharles.Forsyth 	return (c1 == c2);
194837da2899SCharles.Forsyth }
194937da2899SCharles.Forsyth 
195037da2899SCharles.Forsyth /*
195137da2899SCharles.Forsyth  * Return 1 if tag with id m1 ends before tag with id m2,
195237da2899SCharles.Forsyth  * starting at the item after that indexed in ix (but don't
195337da2899SCharles.Forsyth  * modify ix).
195437da2899SCharles.Forsyth  */
195537da2899SCharles.Forsyth static int
tagendsbefore(TkText * tkt,TkTindex * ix,int m1,int m2)195637da2899SCharles.Forsyth tagendsbefore(TkText *tkt, TkTindex *ix, int m1, int m2)
195737da2899SCharles.Forsyth {
195837da2899SCharles.Forsyth 	int s1, s2;
195937da2899SCharles.Forsyth 	TkTindex ix1;
196037da2899SCharles.Forsyth 	TkTitem *i;
196137da2899SCharles.Forsyth 
196237da2899SCharles.Forsyth 	ix1 = *ix;
196337da2899SCharles.Forsyth 	while(tktadjustind(tkt, TkTbyitem, &ix1)) {
196437da2899SCharles.Forsyth 		i = ix1.item;
196537da2899SCharles.Forsyth 		if(i->kind == TkTwin || i->kind == TkTcontline || i->kind == TkTmark)
196637da2899SCharles.Forsyth 			continue;
196737da2899SCharles.Forsyth 		s1 = tkttagset(i, m1);
196837da2899SCharles.Forsyth 		s2 = tkttagset(i, m2);
196937da2899SCharles.Forsyth 		if(!s1)
197037da2899SCharles.Forsyth 			return s2;
197137da2899SCharles.Forsyth 		else if(!s2)
197237da2899SCharles.Forsyth 			return 0;
197337da2899SCharles.Forsyth 	}
197437da2899SCharles.Forsyth 	return 0;
197537da2899SCharles.Forsyth }
197637da2899SCharles.Forsyth 
197737da2899SCharles.Forsyth static int
tktsgmltags(TkText * tkt,Fmt * fmt,TkTitem * iprev,TkTitem * i,TkTindex * ix,int * stack,int * pnstack,int * tmpstack)197837da2899SCharles.Forsyth tktsgmltags(TkText *tkt, Fmt *fmt, TkTitem *iprev, TkTitem *i, TkTindex *ix, int *stack, int *pnstack, int *tmpstack)
197937da2899SCharles.Forsyth {
198037da2899SCharles.Forsyth 	int nprev, n, m, r, k, j, ii, onstack, nt;
198137da2899SCharles.Forsyth 
198237da2899SCharles.Forsyth 	nprev = 0;
198337da2899SCharles.Forsyth 	if(iprev != nil && (iprev->tags[0] != 0 || iprev->tagextra > 0))
198437da2899SCharles.Forsyth 		nprev = 32*(iprev->tagextra + 1);
198537da2899SCharles.Forsyth 	n = 0;
198637da2899SCharles.Forsyth 	if(i != nil && (i->tags[0] != 0 || i->tagextra > 0))
198737da2899SCharles.Forsyth 		n = 32*(i->tagextra + 1);
198837da2899SCharles.Forsyth 	nt = 0;
198937da2899SCharles.Forsyth 	if(n > 0) {
199037da2899SCharles.Forsyth 		/* find tags which open here */
199137da2899SCharles.Forsyth 		for(m = 0; m < n; m++)
199237da2899SCharles.Forsyth 			if(tkttagset(i, m) && (iprev == nil || !tkttagset(iprev, m)))
199337da2899SCharles.Forsyth 				tmpstack[nt++] = m;
199437da2899SCharles.Forsyth 	}
199537da2899SCharles.Forsyth 	if(nprev > 0) {
199637da2899SCharles.Forsyth 		/*
199737da2899SCharles.Forsyth 		 * Find lowest tag in stack that ends before any tag beginning here.
199837da2899SCharles.Forsyth 		 * We have to emit end tags all the way down to there, then add
199937da2899SCharles.Forsyth 		 * back the ones that haven't actually ended here, together with ones
200037da2899SCharles.Forsyth 		 * that start here, and sort all of the added ones so that tags that
200137da2899SCharles.Forsyth 		 * end later are lower in the stack.
200237da2899SCharles.Forsyth 		 */
200337da2899SCharles.Forsyth 		ii = *pnstack;
200437da2899SCharles.Forsyth 		for(k = *pnstack - 1; k >=0; k--) {
200537da2899SCharles.Forsyth 			m = stack[k];
200637da2899SCharles.Forsyth 			if(i == nil || !tkttagset(i, m))
200737da2899SCharles.Forsyth 				ii = k;
200837da2899SCharles.Forsyth 			else
200937da2899SCharles.Forsyth 				for(j = 0; j < nt; j++)
201037da2899SCharles.Forsyth 					if(tagendsbefore(tkt, ix, m, tmpstack[j]))
201137da2899SCharles.Forsyth 						ii = k;
201237da2899SCharles.Forsyth 		}
201337da2899SCharles.Forsyth 		for(k = *pnstack - 1; k >= ii; k--) {
201437da2899SCharles.Forsyth 			m = stack[k];
201537da2899SCharles.Forsyth 			r = fmtprint(fmt, "</%s>", tkttagname(tkt, m));
201637da2899SCharles.Forsyth 			if(r < 0)
201737da2899SCharles.Forsyth 				return r;
201837da2899SCharles.Forsyth 			/* add m back to starting tags if m didn't actually end here */
201937da2899SCharles.Forsyth 			if(i != nil && tkttagset(i, m))
202037da2899SCharles.Forsyth 				tmpstack[nt++] = m;
202137da2899SCharles.Forsyth 		}
202237da2899SCharles.Forsyth 		*pnstack = ii;
202337da2899SCharles.Forsyth 	}
202437da2899SCharles.Forsyth 	if(nt > 0) {
202537da2899SCharles.Forsyth 		/* add tags which open  or reopen here */
202637da2899SCharles.Forsyth 		onstack = *pnstack;
202737da2899SCharles.Forsyth 		k = onstack;
202837da2899SCharles.Forsyth 		for(j = 0; j < nt; j++)
202937da2899SCharles.Forsyth 			stack[k++] = tmpstack[j];
203037da2899SCharles.Forsyth 		*pnstack = k;
203137da2899SCharles.Forsyth 		if(k - onstack > 1) {
203237da2899SCharles.Forsyth 			/* sort new stack entries so tags that end later are lower in stack */
203337da2899SCharles.Forsyth 			for(ii = k-2; ii>= onstack; ii--) {
203437da2899SCharles.Forsyth 				m = stack[ii];
203537da2899SCharles.Forsyth 				for(j = ii+1; j < k && tagendsbefore(tkt, ix, m, stack[j]); j++) {
203637da2899SCharles.Forsyth 					stack[j-1] = stack[j];
203737da2899SCharles.Forsyth 				}
203837da2899SCharles.Forsyth 				stack[j-1] = m;
203937da2899SCharles.Forsyth 			}
204037da2899SCharles.Forsyth 		}
204137da2899SCharles.Forsyth 		for(j = onstack; j < k; j++) {
204237da2899SCharles.Forsyth 			r = fmtprint(fmt, "<%s>", tkttagname(tkt, stack[j]));
204337da2899SCharles.Forsyth 			if(r < 0)
204437da2899SCharles.Forsyth 				return r;
204537da2899SCharles.Forsyth 		}
204637da2899SCharles.Forsyth 	}
204737da2899SCharles.Forsyth 	return 0;
204837da2899SCharles.Forsyth }
204937da2899SCharles.Forsyth 
205037da2899SCharles.Forsyth /*
205137da2899SCharles.Forsyth  * In 'sgml' format, just print text (no special treatment of
205237da2899SCharles.Forsyth  * special characters, except that < turns into &lt;)
205337da2899SCharles.Forsyth  * interspersed with things like <Bold> and </Bold>
205437da2899SCharles.Forsyth  * (where Bold is a tag name).
205537da2899SCharles.Forsyth  * Make sure that the tag pairs nest properly.
205637da2899SCharles.Forsyth */
205737da2899SCharles.Forsyth static char*
tktget(TkText * tkt,TkTindex * ix1,TkTindex * ix2,int sgml,char ** val)205837da2899SCharles.Forsyth tktget(TkText *tkt, TkTindex *ix1, TkTindex *ix2, int sgml, char **val)
205937da2899SCharles.Forsyth {
206037da2899SCharles.Forsyth 	int n, m, i, bychar, nstack;
206137da2899SCharles.Forsyth 	int *stack, *tmpstack;
206237da2899SCharles.Forsyth 	char *s;
206337da2899SCharles.Forsyth 	TkTitem *iprev;
206437da2899SCharles.Forsyth 	Tk *sub;
206537da2899SCharles.Forsyth 	Fmt fmt;
206637da2899SCharles.Forsyth 	char *buf;
206737da2899SCharles.Forsyth 
206837da2899SCharles.Forsyth 	if(!tktindbefore(ix1, ix2))
206937da2899SCharles.Forsyth 		return nil;
207037da2899SCharles.Forsyth 
207137da2899SCharles.Forsyth 	stack = nil;
207237da2899SCharles.Forsyth 	tmpstack = nil;
207337da2899SCharles.Forsyth 
207437da2899SCharles.Forsyth 	iprev = nil;
207537da2899SCharles.Forsyth 	fmtstrinit(&fmt);
207637da2899SCharles.Forsyth 	buf = mallocz(100, 0);
207737da2899SCharles.Forsyth 	if(buf == nil)
207837da2899SCharles.Forsyth 		return TkNomem;
207937da2899SCharles.Forsyth 	if(sgml) {
208037da2899SCharles.Forsyth 		stack = malloc((tkt->nexttag+1)*sizeof(int));
208137da2899SCharles.Forsyth 		tmpstack = malloc((tkt->nexttag+1)*sizeof(int));
208237da2899SCharles.Forsyth 		if(stack == nil || tmpstack == nil)
208337da2899SCharles.Forsyth 			goto nomemret;
208437da2899SCharles.Forsyth 		nstack = 0;
208537da2899SCharles.Forsyth 	}
208637da2899SCharles.Forsyth 	for(;;) {
208737da2899SCharles.Forsyth 		if(ix1->item == ix2->item && ix1->pos == ix2->pos)
208837da2899SCharles.Forsyth 			break;
208937da2899SCharles.Forsyth 		s = nil;
209037da2899SCharles.Forsyth 		bychar = 0;
209137da2899SCharles.Forsyth 		m = 1;
209237da2899SCharles.Forsyth 		switch(ix1->item->kind) {
209337da2899SCharles.Forsyth 		case TkTrune:
209437da2899SCharles.Forsyth 			s = ix1->item->istring;
209537da2899SCharles.Forsyth 			s += tktutfpos(s, ix1->pos);
209637da2899SCharles.Forsyth 			if(ix1->item == ix2->item) {
209737da2899SCharles.Forsyth 				m = ix2->pos - ix1->pos;
209837da2899SCharles.Forsyth 				bychar = 1;
209937da2899SCharles.Forsyth 			}
210037da2899SCharles.Forsyth 			break;
210137da2899SCharles.Forsyth 		case TkTascii:
210237da2899SCharles.Forsyth 			s = ix1->item->istring + ix1->pos;
210337da2899SCharles.Forsyth 			if(ix1->item == ix2->item) {
210437da2899SCharles.Forsyth 				m = ix2->pos - ix1->pos;
210537da2899SCharles.Forsyth 				bychar = 1;
210637da2899SCharles.Forsyth 			}
210737da2899SCharles.Forsyth 			else {
210837da2899SCharles.Forsyth 				m = strlen(s);
210937da2899SCharles.Forsyth 				if(sgml && memchr(s, '<', m) != nil)
211037da2899SCharles.Forsyth 					bychar = 1;
211137da2899SCharles.Forsyth 			}
211237da2899SCharles.Forsyth 			break;
211337da2899SCharles.Forsyth 		case TkTtab:
211437da2899SCharles.Forsyth 			s = "\t";
211537da2899SCharles.Forsyth 			break;
211637da2899SCharles.Forsyth 		case TkTnewline:
211737da2899SCharles.Forsyth 			s = "\n";
211837da2899SCharles.Forsyth 			break;
211937da2899SCharles.Forsyth 		case TkTwin:
212037da2899SCharles.Forsyth 			sub = ix1->item->iwin->sub;
212137da2899SCharles.Forsyth 			if(sgml &&  sub != nil && sub->name != nil) {
212237da2899SCharles.Forsyth 				snprint(buf, 100, "<Window %s>", sub->name->name);
212337da2899SCharles.Forsyth 				s = buf;
212437da2899SCharles.Forsyth 			}
212537da2899SCharles.Forsyth 		}
212637da2899SCharles.Forsyth 		if(s != nil) {
212737da2899SCharles.Forsyth 			if(sgml) {
212837da2899SCharles.Forsyth 				n = tktsgmltags(tkt, &fmt, iprev, ix1->item, ix1, stack, &nstack, tmpstack);
212937da2899SCharles.Forsyth 				if(n < 0)
213037da2899SCharles.Forsyth 					goto nomemret;
213137da2899SCharles.Forsyth 			}
213237da2899SCharles.Forsyth 			if(bychar) {
213337da2899SCharles.Forsyth 				if (ix1->item->kind == TkTrune)
213437da2899SCharles.Forsyth 					n = fmtprint(&fmt, "%.*s", m, s);
213537da2899SCharles.Forsyth 				else {
213637da2899SCharles.Forsyth 					n = 0;
213737da2899SCharles.Forsyth 					for(i = 0; i < m && n >= 0; i++) {
213837da2899SCharles.Forsyth 						if(s[i] == '<')
213937da2899SCharles.Forsyth 							n = fmtprint(&fmt, "&lt;");
214037da2899SCharles.Forsyth 						else
214137da2899SCharles.Forsyth 							n = fmtprint(&fmt, "%c", s[i]);
214237da2899SCharles.Forsyth 					}
214337da2899SCharles.Forsyth 				}
214437da2899SCharles.Forsyth 			}
214537da2899SCharles.Forsyth 			else
214637da2899SCharles.Forsyth 				n = fmtprint(&fmt, "%s", s);
214737da2899SCharles.Forsyth 			if(n < 0)
214837da2899SCharles.Forsyth 				goto nomemret;
214937da2899SCharles.Forsyth 			iprev = ix1->item;
215037da2899SCharles.Forsyth 		}
215137da2899SCharles.Forsyth 		if(ix1->item == ix2->item)
215237da2899SCharles.Forsyth 			break;
215337da2899SCharles.Forsyth 		if(!tktadjustind(tkt, TkTbyitem, ix1)) {
215437da2899SCharles.Forsyth 			if(tktdbg)
215537da2899SCharles.Forsyth 				print("tktextget botch\n");
215637da2899SCharles.Forsyth 			break;
215737da2899SCharles.Forsyth 		}
215837da2899SCharles.Forsyth 	}
215937da2899SCharles.Forsyth 	if(sgml) {
216037da2899SCharles.Forsyth 		n = tktsgmltags(tkt, &fmt, iprev, nil, nil, stack, &nstack, tmpstack);
216137da2899SCharles.Forsyth 		if(n < 0)
216237da2899SCharles.Forsyth 			goto nomemret;
216337da2899SCharles.Forsyth 	}
216437da2899SCharles.Forsyth 
216537da2899SCharles.Forsyth 	*val = fmtstrflush(&fmt);
216637da2899SCharles.Forsyth 	free(buf);
216737da2899SCharles.Forsyth 	return nil;
216837da2899SCharles.Forsyth 
216937da2899SCharles.Forsyth nomemret:
217037da2899SCharles.Forsyth 	free(buf);
217137da2899SCharles.Forsyth 	if(stack != nil)
217237da2899SCharles.Forsyth 		free(stack);
217337da2899SCharles.Forsyth 	if(tmpstack != nil)
217437da2899SCharles.Forsyth 		free(tmpstack);
217537da2899SCharles.Forsyth 	return TkNomem;
217637da2899SCharles.Forsyth }
217737da2899SCharles.Forsyth 
217837da2899SCharles.Forsyth /* Widget Commands (+ means implemented)
217937da2899SCharles.Forsyth 	+bbox
218037da2899SCharles.Forsyth 	+cget
218137da2899SCharles.Forsyth 	+compare
218237da2899SCharles.Forsyth 	+configure
218337da2899SCharles.Forsyth 	+debug
218437da2899SCharles.Forsyth 	+delete
218537da2899SCharles.Forsyth 	+dlineinfo
218637da2899SCharles.Forsyth 	+dump
218737da2899SCharles.Forsyth 	+get
218837da2899SCharles.Forsyth 	+index
218937da2899SCharles.Forsyth 	+insert
219037da2899SCharles.Forsyth 	+mark
219137da2899SCharles.Forsyth 	+scan
219237da2899SCharles.Forsyth 	+search
219337da2899SCharles.Forsyth 	+see
219437da2899SCharles.Forsyth 	+tag
219537da2899SCharles.Forsyth 	+window
219637da2899SCharles.Forsyth 	+xview
219737da2899SCharles.Forsyth 	+yview
219837da2899SCharles.Forsyth */
219937da2899SCharles.Forsyth 
220037da2899SCharles.Forsyth static int
220137da2899SCharles.Forsyth tktviewrectclip(Rectangle *r, Rectangle b);
220237da2899SCharles.Forsyth 
220337da2899SCharles.Forsyth static char*
tktextbbox(Tk * tk,char * arg,char ** val)220437da2899SCharles.Forsyth tktextbbox(Tk *tk, char *arg, char **val)
220537da2899SCharles.Forsyth {
220637da2899SCharles.Forsyth 	char *e;
220737da2899SCharles.Forsyth 	int noclip, w, h;
220837da2899SCharles.Forsyth 	Rectangle r, rview;
220937da2899SCharles.Forsyth 	TkTindex ix;
221037da2899SCharles.Forsyth 	TkText *tkt;
221137da2899SCharles.Forsyth 	char buf[Tkmaxitem];
221237da2899SCharles.Forsyth 
221337da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &ix);
221437da2899SCharles.Forsyth 	if(e != nil)
221537da2899SCharles.Forsyth 		return e;
221637da2899SCharles.Forsyth 
221737da2899SCharles.Forsyth 	noclip = 0;
221837da2899SCharles.Forsyth 	if(*arg != '\0') {
221937da2899SCharles.Forsyth 		/* extension to tk4.0:
222037da2899SCharles.Forsyth 		 * "noclip" means don't clip to viewable area
222137da2899SCharles.Forsyth 		 * "all" means give unclipped bbox of entire contents
222237da2899SCharles.Forsyth 		 */
222337da2899SCharles.Forsyth 		arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
222437da2899SCharles.Forsyth 		if(strcmp(buf, "noclip") == 0)
222537da2899SCharles.Forsyth 			noclip = 1;
222637da2899SCharles.Forsyth 		else
222737da2899SCharles.Forsyth 		if(strcmp(buf, "all") == 0) {
222837da2899SCharles.Forsyth 			tkt = TKobj(TkText, tk);
222937da2899SCharles.Forsyth 			w = tktmaxwid(tkt->start.next);
223037da2899SCharles.Forsyth 			h = tkt->end.orig.y;
223137da2899SCharles.Forsyth 			return tkvalue(val, "0 0 %d %d", w, h);
223237da2899SCharles.Forsyth 		}
223337da2899SCharles.Forsyth 	}
223437da2899SCharles.Forsyth 
223537da2899SCharles.Forsyth 	/*
223637da2899SCharles.Forsyth 	 * skip marks; bbox applies to characters only.
223737da2899SCharles.Forsyth 	 * it's not defined what happens when bbox is applied to a newline char,
223837da2899SCharles.Forsyth 	 * so we'll just let the default case sort that out.
223937da2899SCharles.Forsyth 	 */
224037da2899SCharles.Forsyth 	while (ix.item->kind == TkTmark)
224137da2899SCharles.Forsyth 		ix.item = ix.item->next;
224237da2899SCharles.Forsyth 	r = tktbbox(tk, &ix);
224337da2899SCharles.Forsyth 
224437da2899SCharles.Forsyth 	rview.min.x = 0;
224537da2899SCharles.Forsyth 	rview.min.y = 0;
224637da2899SCharles.Forsyth 	rview.max.x = tk->act.width - tk->ipad.x;
224737da2899SCharles.Forsyth 	rview.max.y = tk->act.height - tk->ipad.y;
224837da2899SCharles.Forsyth 	if(noclip || tktviewrectclip(&r, rview))
224937da2899SCharles.Forsyth 		return tkvalue(val, "%d %d %d %d", r.min.x, r.min.y,
225037da2899SCharles.Forsyth 			r.max.x-r.min.x, r.max.y-r.min.y);
225137da2899SCharles.Forsyth 	return nil;
225237da2899SCharles.Forsyth }
225337da2899SCharles.Forsyth 
225437da2899SCharles.Forsyth /*
225537da2899SCharles.Forsyth  * a supplemented rectclip, as ((0, 1), (0,1)) does not intersect ((0, 0), (5, 5))
225637da2899SCharles.Forsyth  * but for our purposes, we want it to. it's a hack.
225737da2899SCharles.Forsyth  */
225837da2899SCharles.Forsyth static int
tktviewrectclip(Rectangle * rp,Rectangle b)225937da2899SCharles.Forsyth tktviewrectclip(Rectangle *rp, Rectangle b)
226037da2899SCharles.Forsyth {
226137da2899SCharles.Forsyth 	Rectangle *bp = &b;
226237da2899SCharles.Forsyth 	if((rp->min.x<bp->max.x &&
226337da2899SCharles.Forsyth 		(bp->min.x<rp->max.x || (rp->max.x  == b.min.x
226437da2899SCharles.Forsyth 				&& rp->min.x == b.min.x)) &&
226537da2899SCharles.Forsyth 			rp->min.y<bp->max.y && bp->min.y<rp->max.y)==0)
226637da2899SCharles.Forsyth 		return 0;
226737da2899SCharles.Forsyth 	/* They must overlap */
226837da2899SCharles.Forsyth 	if(rp->min.x < bp->min.x)
226937da2899SCharles.Forsyth 		rp->min.x = bp->min.x;
227037da2899SCharles.Forsyth 	if(rp->min.y < bp->min.y)
227137da2899SCharles.Forsyth 		rp->min.y = bp->min.y;
227237da2899SCharles.Forsyth 	if(rp->max.x > bp->max.x)
227337da2899SCharles.Forsyth 		rp->max.x = bp->max.x;
227437da2899SCharles.Forsyth 	if(rp->max.y > bp->max.y)
227537da2899SCharles.Forsyth 		rp->max.y = bp->max.y;
227637da2899SCharles.Forsyth 	return 1;
227737da2899SCharles.Forsyth }
227837da2899SCharles.Forsyth 
227937da2899SCharles.Forsyth static Point
scr2local(Tk * tk,Point p)228037da2899SCharles.Forsyth scr2local(Tk *tk, Point p)
228137da2899SCharles.Forsyth {
228237da2899SCharles.Forsyth 	p = subpt(p, tkposn(tk));
228337da2899SCharles.Forsyth 	p.x -= tk->borderwidth;
228437da2899SCharles.Forsyth 	p.y -= tk->borderwidth;
228537da2899SCharles.Forsyth 	return p;
228637da2899SCharles.Forsyth }
228737da2899SCharles.Forsyth 
228837da2899SCharles.Forsyth static char*
tktextbutton1(Tk * tk,char * arg,char ** val)228937da2899SCharles.Forsyth tktextbutton1(Tk *tk, char *arg, char **val)
229037da2899SCharles.Forsyth {
229137da2899SCharles.Forsyth 	char *e;
229237da2899SCharles.Forsyth 	Point p;
229337da2899SCharles.Forsyth 	TkCtxt *c;
229437da2899SCharles.Forsyth 	TkTindex ix;
229537da2899SCharles.Forsyth 	TkTmarkinfo *mi;
229637da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
229737da2899SCharles.Forsyth 
229837da2899SCharles.Forsyth 	USED(val);
229937da2899SCharles.Forsyth 
230037da2899SCharles.Forsyth 	e = tkxyparse(tk, &arg, &p);
230137da2899SCharles.Forsyth 	if(e != nil)
230237da2899SCharles.Forsyth 		return e;
230337da2899SCharles.Forsyth 	tkt->track = p;
230437da2899SCharles.Forsyth 	p = scr2local(tk, p);
230537da2899SCharles.Forsyth 
230637da2899SCharles.Forsyth 	tktxyind(tk, p.x, p.y, &ix);
230737da2899SCharles.Forsyth 	tkt->tflag &= ~TkTjustfoc;
230837da2899SCharles.Forsyth 	c = tk->env->top->ctxt;
230937da2899SCharles.Forsyth 	if(!(tk->flag&Tkdisabled) && c->tkkeygrab != tk
231037da2899SCharles.Forsyth                       && (tk->name != nil) && ix.item->kind != TkTwin) {
231137da2899SCharles.Forsyth 		tkfocus(tk->env->top, tk->name->name, nil);
231237da2899SCharles.Forsyth 		tkt->tflag |= TkTjustfoc;
231337da2899SCharles.Forsyth 		return nil;
231437da2899SCharles.Forsyth 	}
231537da2899SCharles.Forsyth 
231637da2899SCharles.Forsyth 	mi = tktfindmark(tkt->marks, "insert");
231737da2899SCharles.Forsyth 	if(tktdbg && !mi) {
231837da2899SCharles.Forsyth 		print("tktextbutton1: botch\n");
231937da2899SCharles.Forsyth 		return nil;
232037da2899SCharles.Forsyth 	}
232137da2899SCharles.Forsyth 	tktmarkmove(tk, mi, &ix);
232237da2899SCharles.Forsyth 
232337da2899SCharles.Forsyth 	tktclearsel(tk);
232437da2899SCharles.Forsyth 	tkrepeat(tk, autoselect, nil, TkRptpause, TkRptinterval);
232537da2899SCharles.Forsyth 	return nil;
232637da2899SCharles.Forsyth }
232737da2899SCharles.Forsyth 
232837da2899SCharles.Forsyth static char*
tktextbutton1r(Tk * tk,char * arg,char ** val)232937da2899SCharles.Forsyth tktextbutton1r(Tk *tk, char *arg, char **val)
233037da2899SCharles.Forsyth {
233137da2899SCharles.Forsyth 	TkText *tkt;
233237da2899SCharles.Forsyth 
233337da2899SCharles.Forsyth 	USED(arg);
233437da2899SCharles.Forsyth 	USED(val);
233537da2899SCharles.Forsyth 
233637da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
233737da2899SCharles.Forsyth 	tkt->tflag &= ~TkTnodrag;
233837da2899SCharles.Forsyth 	tkcancelrepeat(tk);
233937da2899SCharles.Forsyth 	return nil;
234037da2899SCharles.Forsyth }
234137da2899SCharles.Forsyth 
234237da2899SCharles.Forsyth static char*
tktextcget(Tk * tk,char * arg,char ** val)234337da2899SCharles.Forsyth tktextcget(Tk *tk, char *arg, char **val)
234437da2899SCharles.Forsyth {
234537da2899SCharles.Forsyth 	TkText *tkt;
234637da2899SCharles.Forsyth 	TkOptab tko[3];
234737da2899SCharles.Forsyth 
234837da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
234937da2899SCharles.Forsyth 	tko[0].ptr = tk;
235037da2899SCharles.Forsyth 	tko[0].optab = tkgeneric;
235137da2899SCharles.Forsyth 	tko[1].ptr = tkt;
235237da2899SCharles.Forsyth 	tko[1].optab = textopts;
235337da2899SCharles.Forsyth 	tko[2].ptr = nil;
235437da2899SCharles.Forsyth 
235537da2899SCharles.Forsyth 	return tkgencget(tko, arg, val, tk->env->top);
235637da2899SCharles.Forsyth }
235737da2899SCharles.Forsyth 
235837da2899SCharles.Forsyth static char*
tktextcompare(Tk * tk,char * arg,char ** val)235937da2899SCharles.Forsyth tktextcompare(Tk *tk, char *arg, char **val)
236037da2899SCharles.Forsyth {
236137da2899SCharles.Forsyth 	int op;
236237da2899SCharles.Forsyth 	char *e;
236337da2899SCharles.Forsyth 	TkTindex i1, i2;
236437da2899SCharles.Forsyth 	TkText *tkt;
236537da2899SCharles.Forsyth 	TkStab *s;
236637da2899SCharles.Forsyth 	char *buf;
236737da2899SCharles.Forsyth 
236837da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
236937da2899SCharles.Forsyth 
237037da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &i1);
237137da2899SCharles.Forsyth 	if(e != nil)
237237da2899SCharles.Forsyth 		return e;
237337da2899SCharles.Forsyth 
237437da2899SCharles.Forsyth 	if(*arg == '\0')
237537da2899SCharles.Forsyth 		return TkBadcm;
237637da2899SCharles.Forsyth 
237737da2899SCharles.Forsyth 	buf = mallocz(Tkmaxitem, 0);
237837da2899SCharles.Forsyth 	if(buf == nil)
237937da2899SCharles.Forsyth 		return TkNomem;
238037da2899SCharles.Forsyth 
238137da2899SCharles.Forsyth 	arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
238237da2899SCharles.Forsyth 
238337da2899SCharles.Forsyth 	op = -1;
238437da2899SCharles.Forsyth 	for(s = tkcompare; s->val; s++)
238537da2899SCharles.Forsyth 		if(strcmp(s->val, buf) == 0) {
238637da2899SCharles.Forsyth 			op = s->con;
238737da2899SCharles.Forsyth 			break;
238837da2899SCharles.Forsyth 		}
238937da2899SCharles.Forsyth 	if(op == -1) {
239037da2899SCharles.Forsyth 		free(buf);
239137da2899SCharles.Forsyth 		return TkBadcm;
239237da2899SCharles.Forsyth 	}
239337da2899SCharles.Forsyth 
239437da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &i2);
239537da2899SCharles.Forsyth 	if(e != nil) {
239637da2899SCharles.Forsyth 		free(buf);
239737da2899SCharles.Forsyth 		return e;
239837da2899SCharles.Forsyth 	}
239937da2899SCharles.Forsyth 
240037da2899SCharles.Forsyth 	e = tkvalue(val, tktindcompare(tkt, &i1, op, &i2)? "1" : "0");
240137da2899SCharles.Forsyth 	free(buf);
240237da2899SCharles.Forsyth 	return e;
240337da2899SCharles.Forsyth }
240437da2899SCharles.Forsyth 
240537da2899SCharles.Forsyth static char*
tktextconfigure(Tk * tk,char * arg,char ** val)240637da2899SCharles.Forsyth tktextconfigure(Tk *tk, char *arg, char **val)
240737da2899SCharles.Forsyth {
240837da2899SCharles.Forsyth 	char *e;
240937da2899SCharles.Forsyth 	TkGeom g;
241037da2899SCharles.Forsyth 	int bd;
241137da2899SCharles.Forsyth 	TkText *tkt;
241237da2899SCharles.Forsyth 	TkOptab tko[3];
241337da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
241437da2899SCharles.Forsyth 	tko[0].ptr = tk;
241537da2899SCharles.Forsyth 	tko[0].optab = tkgeneric;
241637da2899SCharles.Forsyth 	tko[1].ptr = tkt;
241737da2899SCharles.Forsyth 	tko[1].optab = textopts;
241837da2899SCharles.Forsyth 	tko[2].ptr = nil;
241937da2899SCharles.Forsyth 
242037da2899SCharles.Forsyth 	if(*arg == '\0')
242137da2899SCharles.Forsyth 		return tkconflist(tko, val);
242237da2899SCharles.Forsyth 
242337da2899SCharles.Forsyth 	g = tk->req;
242437da2899SCharles.Forsyth 	bd = tk->borderwidth;
242537da2899SCharles.Forsyth 
242637da2899SCharles.Forsyth 	e = tkparse(tk->env->top, arg, tko, nil);
242737da2899SCharles.Forsyth 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
242837da2899SCharles.Forsyth 	if (tkt->propagate != BoolT) {
242937da2899SCharles.Forsyth 		if ((tk->flag & Tksetwidth) == 0)
243037da2899SCharles.Forsyth 			tk->req.width = tk->env->wzero*Textwidth;
243137da2899SCharles.Forsyth 		if ((tk->flag & Tksetheight) == 0)
243237da2899SCharles.Forsyth 			tk->req.height = tk->env->font->height*Textheight;
243337da2899SCharles.Forsyth 	}
243437da2899SCharles.Forsyth 	/* note: tkgeomchg() may also call tktfixgeom() via tktextgeom() */
243537da2899SCharles.Forsyth 	tktfixgeom(tk, &tkt->start, tkt->end.prev, 0);
243637da2899SCharles.Forsyth 	tktextsize(tk, 0);
243737da2899SCharles.Forsyth 	tkgeomchg(tk, &g, bd);
243837da2899SCharles.Forsyth 	tktnotdrawn(tk, 0, tkt->end.orig.y, 1);
243937da2899SCharles.Forsyth 
244037da2899SCharles.Forsyth 	return e;
244137da2899SCharles.Forsyth }
244237da2899SCharles.Forsyth 
244337da2899SCharles.Forsyth static char*
tktextdebug(Tk * tk,char * arg,char ** val)244437da2899SCharles.Forsyth tktextdebug(Tk *tk, char *arg, char **val)
244537da2899SCharles.Forsyth {
244637da2899SCharles.Forsyth 	char buf[Tkmaxitem];
244737da2899SCharles.Forsyth 
244837da2899SCharles.Forsyth 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
244937da2899SCharles.Forsyth 	if(*buf == '\0')
245037da2899SCharles.Forsyth 		return tkvalue(val, "%s", tktdbg? "on" : "off");
245137da2899SCharles.Forsyth 	else {
245237da2899SCharles.Forsyth 		tktdbg = (strcmp(buf, "1") == 0 || strcmp(buf, "yes") == 0);
245337da2899SCharles.Forsyth 		if(tktdbg) {
245437da2899SCharles.Forsyth 			tktprinttext(TKobj(TkText, tk));
245537da2899SCharles.Forsyth 		}
245637da2899SCharles.Forsyth 		return nil;
245737da2899SCharles.Forsyth 	}
245837da2899SCharles.Forsyth }
245937da2899SCharles.Forsyth 
246037da2899SCharles.Forsyth static char*
tktextdelete(Tk * tk,char * arg,char ** val)246137da2899SCharles.Forsyth tktextdelete(Tk *tk, char *arg, char **val)
246237da2899SCharles.Forsyth {
246337da2899SCharles.Forsyth 	int sameit;
246437da2899SCharles.Forsyth 	char *e;
246537da2899SCharles.Forsyth 	TkTindex i1, i2, ip, isee;
246637da2899SCharles.Forsyth 	TkTline *lmin;
246737da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
246837da2899SCharles.Forsyth 	char buf[20], *p;
246937da2899SCharles.Forsyth 
247037da2899SCharles.Forsyth 	USED(val);
247137da2899SCharles.Forsyth 
247237da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &i1);
247337da2899SCharles.Forsyth 	if(e != nil)
247437da2899SCharles.Forsyth 		return e;
247537da2899SCharles.Forsyth 	tktadjustind(tkt, TkTbycharstart, &i1);
247637da2899SCharles.Forsyth 
247737da2899SCharles.Forsyth 	e = tktsplititem(&i1);
247837da2899SCharles.Forsyth 	if(e != nil)
247937da2899SCharles.Forsyth 		return e;
248037da2899SCharles.Forsyth 
248137da2899SCharles.Forsyth 	if(*arg != '\0') {
248237da2899SCharles.Forsyth 		e = tktindparse(tk, &arg, &i2);
248337da2899SCharles.Forsyth 		if(e != nil)
248437da2899SCharles.Forsyth 			return e;
248537da2899SCharles.Forsyth 	}
248637da2899SCharles.Forsyth 	else {
248737da2899SCharles.Forsyth 		i2 = i1;
248837da2899SCharles.Forsyth 		tktadjustind(tkt, TkTbychar, &i2);
248937da2899SCharles.Forsyth 	}
249037da2899SCharles.Forsyth 	if(tktindcompare(tkt, &i1, TkGte, &i2))
249137da2899SCharles.Forsyth 		return nil;
249237da2899SCharles.Forsyth 
249337da2899SCharles.Forsyth 	sameit = (i1.item == i2.item);
249437da2899SCharles.Forsyth 
249537da2899SCharles.Forsyth 	/* save possible fixup see place */
249637da2899SCharles.Forsyth 	isee.line = nil;
249737da2899SCharles.Forsyth 	if(i2.line->orig.y + i2.line->height < tkt->deltatv.y) {
249837da2899SCharles.Forsyth 		/* delete completely precedes view */
249937da2899SCharles.Forsyth 		tktxyind(tk, 0, 0, &isee);
250037da2899SCharles.Forsyth 	}
250137da2899SCharles.Forsyth 
250237da2899SCharles.Forsyth 	e = tktsplititem(&i2);
250337da2899SCharles.Forsyth 	if(e != nil)
250437da2899SCharles.Forsyth 		return e;
250537da2899SCharles.Forsyth 
250637da2899SCharles.Forsyth 	if(sameit) {
250737da2899SCharles.Forsyth 		/* after split, i1 should be in previous item to i2 */
250837da2899SCharles.Forsyth 		ip = i2;
250937da2899SCharles.Forsyth 		tktadjustind(tkt, TkTbyitemback, &ip);
251037da2899SCharles.Forsyth 		i1.item = ip.item;
251137da2899SCharles.Forsyth 	}
251237da2899SCharles.Forsyth 
251337da2899SCharles.Forsyth 	lmin = tktprevwrapline(tk, i1.line);
251437da2899SCharles.Forsyth 	while(i1.item != i2.item) {
251537da2899SCharles.Forsyth 		if(i1.item->kind != TkTmark)
251637da2899SCharles.Forsyth 			tktremitem(tkt, &i1);
251737da2899SCharles.Forsyth 			/* tktremitem moves i1 to next item */
251837da2899SCharles.Forsyth 		else {
251937da2899SCharles.Forsyth 			if(!tktadjustind(tkt, TkTbyitem, &i1)) {
252037da2899SCharles.Forsyth 				if(tktdbg)
252137da2899SCharles.Forsyth 					print("tktextdelete botch\n");
252237da2899SCharles.Forsyth 				break;
252337da2899SCharles.Forsyth 			}
252437da2899SCharles.Forsyth 		}
252537da2899SCharles.Forsyth 	}
252637da2899SCharles.Forsyth 
252737da2899SCharles.Forsyth 	/*
252837da2899SCharles.Forsyth 	 * guard against invalidation of index by tktfixgeom
252937da2899SCharles.Forsyth 	 */
253037da2899SCharles.Forsyth 	if (isee.line != nil)
253137da2899SCharles.Forsyth 		snprint(buf, sizeof(buf), "%d.%d", tktlinenum(tkt, &isee), tktlinepos(tkt, &isee));
253237da2899SCharles.Forsyth 
253337da2899SCharles.Forsyth 	tktfixgeom(tk, lmin, i1.line, 0);
253437da2899SCharles.Forsyth 	tktextsize(tk, 1);
253537da2899SCharles.Forsyth 	if(isee.line != nil) {
253637da2899SCharles.Forsyth 		p = buf;
253737da2899SCharles.Forsyth 		tktindparse(tk, &p, &isee);
253837da2899SCharles.Forsyth 		tktsee(tk, &isee, 1);
253937da2899SCharles.Forsyth 	}
254037da2899SCharles.Forsyth 	return nil;
254137da2899SCharles.Forsyth }
254237da2899SCharles.Forsyth 
254337da2899SCharles.Forsyth static char*
tktextsee(Tk * tk,char * arg,char ** val)254437da2899SCharles.Forsyth tktextsee(Tk *tk, char *arg, char **val)
254537da2899SCharles.Forsyth {
254637da2899SCharles.Forsyth 	char *e;
254737da2899SCharles.Forsyth 	TkTindex ix;
254837da2899SCharles.Forsyth 
254937da2899SCharles.Forsyth 	USED(val);
255037da2899SCharles.Forsyth 
255137da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &ix);
255237da2899SCharles.Forsyth 	if(e != nil)
255337da2899SCharles.Forsyth 		return e;
255437da2899SCharles.Forsyth 
255537da2899SCharles.Forsyth 	tktsee(tk, &ix, 0);
255637da2899SCharles.Forsyth 	return nil;
255737da2899SCharles.Forsyth }
255837da2899SCharles.Forsyth 
255937da2899SCharles.Forsyth static char*
tktextdelins(Tk * tk,char * arg,char ** val)256037da2899SCharles.Forsyth tktextdelins(Tk *tk, char *arg, char **val)
256137da2899SCharles.Forsyth {
256237da2899SCharles.Forsyth 	int m, c, skipping, wordc, n;
256337da2899SCharles.Forsyth 	TkTindex ix, ix2;
256437da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
256537da2899SCharles.Forsyth 	char buf[30];
256637da2899SCharles.Forsyth 
256737da2899SCharles.Forsyth 	USED(val);
256837da2899SCharles.Forsyth 
256937da2899SCharles.Forsyth 	if(tk->flag&Tkdisabled)
257037da2899SCharles.Forsyth 		return nil;
257137da2899SCharles.Forsyth 
257237da2899SCharles.Forsyth 	if(tktgetsel(tk, &ix, &ix2))
257337da2899SCharles.Forsyth 		tktextdelete(tk, "sel.first sel.last", nil);
257437da2899SCharles.Forsyth 	else {
257537da2899SCharles.Forsyth 		while(*arg == ' ')
257637da2899SCharles.Forsyth 			arg++;
257737da2899SCharles.Forsyth 		if(*arg == '-') {
257837da2899SCharles.Forsyth 			m = arg[1];
257937da2899SCharles.Forsyth 			if(m == 'c')
258037da2899SCharles.Forsyth 				n = 1;
258137da2899SCharles.Forsyth 			else {
258237da2899SCharles.Forsyth 				/* delete prev word (m=='w') or prev line (m=='l') */
258337da2899SCharles.Forsyth 				if(!tktmarkind(tk, "insert", &ix))
258437da2899SCharles.Forsyth 					return nil;
258537da2899SCharles.Forsyth 				if(!tktadjustind(tkt, TkTbycharback, &ix))
258637da2899SCharles.Forsyth 					return nil;
258737da2899SCharles.Forsyth 				n = 1;
258837da2899SCharles.Forsyth 				/* ^W skips back over nonwordchars, then takes maximal seq of wordchars */
258937da2899SCharles.Forsyth 				skipping = 1;
259037da2899SCharles.Forsyth 				for(;;) {
259137da2899SCharles.Forsyth 					c = tktindrune(&ix);
259237da2899SCharles.Forsyth 					if(c == '\n') {
259337da2899SCharles.Forsyth 						/* special case: always delete at least one char */
259437da2899SCharles.Forsyth 						if(n > 1)
259537da2899SCharles.Forsyth 							n--;
259637da2899SCharles.Forsyth 						break;
259737da2899SCharles.Forsyth 					}
259837da2899SCharles.Forsyth 					if(m == 'w') {
259937da2899SCharles.Forsyth 						wordc = tkiswordchar(c);
260037da2899SCharles.Forsyth 						if(wordc && skipping)
260137da2899SCharles.Forsyth 							skipping = 0;
260237da2899SCharles.Forsyth 						else if(!wordc && !skipping) {
260337da2899SCharles.Forsyth 							n--;
260437da2899SCharles.Forsyth 							break;
260537da2899SCharles.Forsyth 						}
260637da2899SCharles.Forsyth 					}
260737da2899SCharles.Forsyth 					if(tktadjustind(tkt, TkTbycharback, &ix))
260837da2899SCharles.Forsyth 						n++;
260937da2899SCharles.Forsyth 					else
261037da2899SCharles.Forsyth 						break;
261137da2899SCharles.Forsyth 				}
261237da2899SCharles.Forsyth 			}
261337da2899SCharles.Forsyth 			sprint(buf, "insert-%dc insert", n);
261437da2899SCharles.Forsyth 			tktextdelete(tk, buf, nil);
261537da2899SCharles.Forsyth 		}
2616*3a78c580Sforsyth 		else if(arg[0] == '+' && arg[1] == 'l')
2617*3a78c580Sforsyth 			tktextdelete(tk, "insert {insert lineend}", nil);
261837da2899SCharles.Forsyth 		else
261937da2899SCharles.Forsyth 			tktextdelete(tk, "insert", nil);
262037da2899SCharles.Forsyth 		tktextsee(tk, "insert", nil);
262137da2899SCharles.Forsyth 	}
262237da2899SCharles.Forsyth 	return nil;
262337da2899SCharles.Forsyth }
262437da2899SCharles.Forsyth 
262537da2899SCharles.Forsyth static char*
tktextdlineinfo(Tk * tk,char * arg,char ** val)262637da2899SCharles.Forsyth tktextdlineinfo(Tk *tk, char *arg, char **val)
262737da2899SCharles.Forsyth {
262837da2899SCharles.Forsyth 	char *e;
262937da2899SCharles.Forsyth 	TkTindex ix;
263037da2899SCharles.Forsyth 	TkTline *l;
263137da2899SCharles.Forsyth 	Point p;
263237da2899SCharles.Forsyth 	int vh;
263337da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
263437da2899SCharles.Forsyth 
263537da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &ix);
263637da2899SCharles.Forsyth 	if(e != nil)
263737da2899SCharles.Forsyth 		return e;
263837da2899SCharles.Forsyth 
263937da2899SCharles.Forsyth 	l = ix.line;
264037da2899SCharles.Forsyth 	vh = tk->act.height;
264137da2899SCharles.Forsyth 
264237da2899SCharles.Forsyth 	/* get p in V space */
264337da2899SCharles.Forsyth 	p = subpt(l->orig, tkt->deltatv);
264437da2899SCharles.Forsyth 	if(p.y+l->height < 0 || p.y >= vh)
264537da2899SCharles.Forsyth 		return nil;
264637da2899SCharles.Forsyth 
264737da2899SCharles.Forsyth 	return tkvalue(val, "%d %d %d %d %d",
264837da2899SCharles.Forsyth 		p.x, p.y, l->width, l->height, l->ascent);
264937da2899SCharles.Forsyth }
265037da2899SCharles.Forsyth 
265137da2899SCharles.Forsyth static char*
tktextdump(Tk * tk,char * arg,char ** val)265237da2899SCharles.Forsyth tktextdump(Tk *tk, char *arg, char **val)
265337da2899SCharles.Forsyth {
265437da2899SCharles.Forsyth 	TkTline *l;
265537da2899SCharles.Forsyth 	TkTitem *i;
265637da2899SCharles.Forsyth 	Fmt fmt;
265737da2899SCharles.Forsyth 	TkText *tkt;
265837da2899SCharles.Forsyth 	TkDump tkdump;
265937da2899SCharles.Forsyth 	TkOptab tko[2];
266037da2899SCharles.Forsyth 	TkTtaginfo *ti;
266137da2899SCharles.Forsyth 	TkName *names, *n;
266237da2899SCharles.Forsyth 	char *e, *win, *p;
266337da2899SCharles.Forsyth 	TkTindex ix1, ix2;
266437da2899SCharles.Forsyth 	int r, j, numitems;
266537da2899SCharles.Forsyth 	ulong fg, bg;
266637da2899SCharles.Forsyth 
266737da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
266837da2899SCharles.Forsyth 
266937da2899SCharles.Forsyth 
267037da2899SCharles.Forsyth 	tkdump.sgml = 0;
267137da2899SCharles.Forsyth 	tkdump.metrics = 0;
267237da2899SCharles.Forsyth 
267337da2899SCharles.Forsyth 	tko[0].ptr = &tkdump;
267437da2899SCharles.Forsyth 	tko[0].optab = dumpopts;
267537da2899SCharles.Forsyth 	tko[1].ptr = nil;
267637da2899SCharles.Forsyth 	names = nil;
267737da2899SCharles.Forsyth 	e = tkparse(tk->env->top, arg, tko, &names);
267837da2899SCharles.Forsyth 	if(e != nil)
267937da2899SCharles.Forsyth 		return e;
268037da2899SCharles.Forsyth 
268137da2899SCharles.Forsyth 	if(names != nil) {			/* supplied indices */
268237da2899SCharles.Forsyth 		p = names->name;
268337da2899SCharles.Forsyth 		e = tktindparse(tk, &p, &ix1);
268437da2899SCharles.Forsyth 		if(e != nil) {
268537da2899SCharles.Forsyth 			tkfreename(names);
268637da2899SCharles.Forsyth 			return e;
268737da2899SCharles.Forsyth 		}
268837da2899SCharles.Forsyth 		n = names->link;
268937da2899SCharles.Forsyth 		if(n != nil) {
269037da2899SCharles.Forsyth 			p = n->name;
269137da2899SCharles.Forsyth 			e = tktindparse(tk, &p, &ix2);
269237da2899SCharles.Forsyth 			if(e != nil) {
269337da2899SCharles.Forsyth 				tkfreename(names);
269437da2899SCharles.Forsyth 				return e;
269537da2899SCharles.Forsyth 			}
269637da2899SCharles.Forsyth 		}
269737da2899SCharles.Forsyth 		else {
269837da2899SCharles.Forsyth 			ix2 = ix1;
269937da2899SCharles.Forsyth 			tktadjustind(tkt, TkTbychar, &ix2);
270037da2899SCharles.Forsyth 		}
270137da2899SCharles.Forsyth 		tkfreename(names);
270237da2899SCharles.Forsyth 		if(!tktindbefore(&ix1, &ix2))
270337da2899SCharles.Forsyth 			return nil;
270437da2899SCharles.Forsyth 	}
270537da2899SCharles.Forsyth 	else
270637da2899SCharles.Forsyth 		return TkBadix;
270737da2899SCharles.Forsyth 
270837da2899SCharles.Forsyth 	if(tkdump.metrics != 0) {
270937da2899SCharles.Forsyth 		fmtstrinit(&fmt);
271037da2899SCharles.Forsyth 		if(fmtprint(&fmt, "%%Fonts\n") < 0)
271137da2899SCharles.Forsyth 			return TkNomem;
271237da2899SCharles.Forsyth 		for(ti=tkt->tags; ti != nil; ti=ti->next) {
271337da2899SCharles.Forsyth 			if(ti->env == nil || ti->env->font == nil)
271437da2899SCharles.Forsyth 				continue;
271537da2899SCharles.Forsyth 			if(fmtprint(&fmt, "%d::%s\n", ti->id,ti->env->font->name) < 0)
271637da2899SCharles.Forsyth 				return TkNomem;
271737da2899SCharles.Forsyth 		}
271837da2899SCharles.Forsyth 		if(fmtprint(&fmt, "-1::%s\n%%Colors\n", tk->env->font->name) < 0)
271937da2899SCharles.Forsyth 			return TkNomem;
272037da2899SCharles.Forsyth 		for(ti=tkt->tags; ti != nil; ti=ti->next) {
272137da2899SCharles.Forsyth 			if(ti->env == nil)
272237da2899SCharles.Forsyth 				continue;
272337da2899SCharles.Forsyth 			bg = ti->env->colors[TkCbackgnd];
272437da2899SCharles.Forsyth 			fg = ti->env->colors[TkCforegnd];
272537da2899SCharles.Forsyth 			if(bg == tk->env->colors[TkCbackgnd] &&
272637da2899SCharles.Forsyth 			   fg == ti->env->colors[TkCforegnd])
272737da2899SCharles.Forsyth 				continue;
272837da2899SCharles.Forsyth 			r = fmtprint(&fmt,"%d::#%.8lux\n", ti->id, bg);
272937da2899SCharles.Forsyth 			if(r < 0)
273037da2899SCharles.Forsyth 				return TkNomem;
273137da2899SCharles.Forsyth 			r = fmtprint(&fmt,"%d::#%.8lux\n", ti->id, fg);
273237da2899SCharles.Forsyth 			if(r < 0)
273337da2899SCharles.Forsyth 				return TkNomem;
273437da2899SCharles.Forsyth 		}
273537da2899SCharles.Forsyth 		if(fmtprint(&fmt, "%%Lines\n") < 0)
273637da2899SCharles.Forsyth 			return TkNomem;
273737da2899SCharles.Forsyth 
273837da2899SCharles.Forsyth 		/*
273937da2899SCharles.Forsyth 		 * In 'metrics' format lines are recorded in the following way:
274037da2899SCharles.Forsyth 		 *    xorig yorig wd ht as [data]
274137da2899SCharles.Forsyth 		 * where data is of the form:
274237da2899SCharles.Forsyth 		 *    CodeWidth{tags} data
274337da2899SCharles.Forsyth 		 * For Example;
274437da2899SCharles.Forsyth 		 *    A200{200000} Hello World!
274537da2899SCharles.Forsyth 		 * denotes an A(scii) contiguous string of 200 pixels with
274637da2899SCharles.Forsyth 		 * bit 20 set in its tags which corresponds to some font.
274737da2899SCharles.Forsyth 		 *
274837da2899SCharles.Forsyth 	 	*/
274937da2899SCharles.Forsyth 		if(ix2.line->items != ix2.item)
275037da2899SCharles.Forsyth 			ix2.line = ix2.line->next;
275137da2899SCharles.Forsyth 		for(l = ix1.line; l != ix2.line; l = l->next) {
275237da2899SCharles.Forsyth 			numitems = 0;
275337da2899SCharles.Forsyth 			for(i = l->items; i != nil; i = i->next) {
275437da2899SCharles.Forsyth 				if(i->kind != TkTmark)
275537da2899SCharles.Forsyth 					numitems++;
275637da2899SCharles.Forsyth 			}
275737da2899SCharles.Forsyth 			r = fmtprint(&fmt, "%d %d %d %d %d %d ",
275837da2899SCharles.Forsyth 				l->orig.x, l->orig.y, l->width, l->height, l->ascent,numitems);
275937da2899SCharles.Forsyth 			if(r < 0)
276037da2899SCharles.Forsyth 				return TkNomem;
276137da2899SCharles.Forsyth 			for(i = l->items; i != nil; i = i->next) {
276237da2899SCharles.Forsyth 				switch(i->kind) {
276337da2899SCharles.Forsyth 				case TkTascii:
276437da2899SCharles.Forsyth 				case TkTrune:
276537da2899SCharles.Forsyth 					r = i->kind == TkTascii ? 'A' : 'R';
276637da2899SCharles.Forsyth 					if(fmtprint(&fmt,"[%c%d{", r, i->width) < 0)
276737da2899SCharles.Forsyth 						return TkNomem;
276837da2899SCharles.Forsyth 					if(i->tags !=0 || i->tagextra !=0) {
276937da2899SCharles.Forsyth 						if(fmtprint(&fmt,"%lux", i->tags[0]) < 0)
277037da2899SCharles.Forsyth 							return TkNomem;
277137da2899SCharles.Forsyth 						for(j=0; j < i->tagextra; j++)
277237da2899SCharles.Forsyth 							if(fmtprint(&fmt,"::%lux", i->tags[j+1]) < 0)
277337da2899SCharles.Forsyth 								return TkNomem;
277437da2899SCharles.Forsyth 					}
277537da2899SCharles.Forsyth 					/* XXX string should be quoted to avoid embedded ']'s */
277637da2899SCharles.Forsyth 					if(fmtprint(&fmt,"}%s]", i->istring) < 0)
277737da2899SCharles.Forsyth 						return TkNomem;
277837da2899SCharles.Forsyth 					break;
277937da2899SCharles.Forsyth 				case TkTnewline:
278037da2899SCharles.Forsyth 				case TkTcontline:
278137da2899SCharles.Forsyth 					r = i->kind == TkTnewline ? 'N' : 'C';
278237da2899SCharles.Forsyth 					if(fmtprint(&fmt, "[%c]", r) < 0)
278337da2899SCharles.Forsyth 						return TkNomem;
278437da2899SCharles.Forsyth 					break;
278537da2899SCharles.Forsyth 				case TkTtab:
278637da2899SCharles.Forsyth 					if(fmtprint(&fmt,"[T%d]",i->width) < 0)
278737da2899SCharles.Forsyth 						return TkNomem;
278837da2899SCharles.Forsyth 					break;
278937da2899SCharles.Forsyth 				case TkTwin:
279037da2899SCharles.Forsyth 					win = "<null>";
279137da2899SCharles.Forsyth 					if(i->iwin->sub != nil)
279237da2899SCharles.Forsyth 						win = i->iwin->sub->name->name;
279337da2899SCharles.Forsyth 					if(fmtprint(&fmt,"[W%d %s]",i->width, win) < 0)
279437da2899SCharles.Forsyth 						return TkNomem;
279537da2899SCharles.Forsyth 					break;
279637da2899SCharles.Forsyth 				}
279737da2899SCharles.Forsyth 				if(fmtprint(&fmt, " ") < 0)
279837da2899SCharles.Forsyth 					return TkNomem;
279937da2899SCharles.Forsyth 
280037da2899SCharles.Forsyth 			}
280137da2899SCharles.Forsyth 			if(fmtprint(&fmt, "\n") < 0)
280237da2899SCharles.Forsyth 				return TkNomem;
280337da2899SCharles.Forsyth 			*val = fmtstrflush(&fmt);
280437da2899SCharles.Forsyth 			if(*val == nil)
280537da2899SCharles.Forsyth 				return TkNomem;
280637da2899SCharles.Forsyth 		}
280737da2899SCharles.Forsyth 	}
280837da2899SCharles.Forsyth 	else
280937da2899SCharles.Forsyth 		return tktget(tkt, &ix1, &ix2, tkdump.sgml, val);
281037da2899SCharles.Forsyth 
281137da2899SCharles.Forsyth 	return nil;
281237da2899SCharles.Forsyth }
281337da2899SCharles.Forsyth 
281437da2899SCharles.Forsyth 
281537da2899SCharles.Forsyth static char*
tktextget(Tk * tk,char * arg,char ** val)281637da2899SCharles.Forsyth tktextget(Tk *tk, char *arg, char **val)
281737da2899SCharles.Forsyth {
281837da2899SCharles.Forsyth 	char *e;
281937da2899SCharles.Forsyth 	TkTindex ix1, ix2;
282037da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
282137da2899SCharles.Forsyth 
282237da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &ix1);
282337da2899SCharles.Forsyth 	if(e != nil)
282437da2899SCharles.Forsyth 		return e;
282537da2899SCharles.Forsyth 
282637da2899SCharles.Forsyth 	if(*arg != '\0') {
282737da2899SCharles.Forsyth 		e = tktindparse(tk, &arg, &ix2);
282837da2899SCharles.Forsyth 		if(e != nil)
282937da2899SCharles.Forsyth 			return e;
283037da2899SCharles.Forsyth 	}
283137da2899SCharles.Forsyth 	else {
283237da2899SCharles.Forsyth 		ix2 = ix1;
283337da2899SCharles.Forsyth 		tktadjustind(tkt, TkTbychar, &ix2);
283437da2899SCharles.Forsyth 	}
283537da2899SCharles.Forsyth 	return tktget(tkt, &ix1, &ix2, 0, val);
283637da2899SCharles.Forsyth }
283737da2899SCharles.Forsyth 
283837da2899SCharles.Forsyth static char*
tktextindex(Tk * tk,char * arg,char ** val)283937da2899SCharles.Forsyth tktextindex(Tk *tk, char *arg, char **val)
284037da2899SCharles.Forsyth {
284137da2899SCharles.Forsyth 	char *e;
284237da2899SCharles.Forsyth 	TkTindex ix;
284337da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
284437da2899SCharles.Forsyth 
284537da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &ix);
284637da2899SCharles.Forsyth 	if(e != nil)
284737da2899SCharles.Forsyth 		return e;
284837da2899SCharles.Forsyth 	return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix), tktlinepos(tkt, &ix));
284937da2899SCharles.Forsyth }
285037da2899SCharles.Forsyth 
285137da2899SCharles.Forsyth static char*
tktextinsert(Tk * tk,char * arg,char ** val)285237da2899SCharles.Forsyth tktextinsert(Tk *tk, char *arg, char **val)
285337da2899SCharles.Forsyth {
285437da2899SCharles.Forsyth 	int n;
285537da2899SCharles.Forsyth 	char *e, *p, *pe;
285637da2899SCharles.Forsyth 	TkTindex ins, pins;
285737da2899SCharles.Forsyth 	TkTtaginfo *ti;
285837da2899SCharles.Forsyth 	TkText *tkt;
285937da2899SCharles.Forsyth 	TkTline *lmin;
286037da2899SCharles.Forsyth 	TkTop *top;
286137da2899SCharles.Forsyth 	TkTitem *tagit;
286237da2899SCharles.Forsyth 	char *tbuf, *buf;
286337da2899SCharles.Forsyth 
286437da2899SCharles.Forsyth 	USED(val);
286537da2899SCharles.Forsyth 
286637da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
286737da2899SCharles.Forsyth 	top = tk->env->top;
286837da2899SCharles.Forsyth 
286937da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &ins);
287037da2899SCharles.Forsyth 	if(e != nil)
287137da2899SCharles.Forsyth 		return e;
287237da2899SCharles.Forsyth 
287337da2899SCharles.Forsyth 	if(ins.item->kind == TkTmark) {
287437da2899SCharles.Forsyth 		if(ins.item->imark->gravity == Tkleft) {
287537da2899SCharles.Forsyth 			while(ins.item->kind == TkTmark && ins.item->imark->gravity == Tkleft)
287637da2899SCharles.Forsyth 				if(!tktadjustind(tkt, TkTbyitem, &ins)) {
287737da2899SCharles.Forsyth 					if(tktdbg)
287837da2899SCharles.Forsyth 						print("tktextinsert botch\n");
287937da2899SCharles.Forsyth 					break;
288037da2899SCharles.Forsyth 				}
288137da2899SCharles.Forsyth 		}
288237da2899SCharles.Forsyth 		else {
288337da2899SCharles.Forsyth 			for(;;) {
288437da2899SCharles.Forsyth 				pins = ins;
288537da2899SCharles.Forsyth 				if(!tktadjustind(tkt, TkTbyitemback, &pins))
288637da2899SCharles.Forsyth 					break;
288737da2899SCharles.Forsyth 				if(pins.item->kind == TkTmark && pins.item->imark->gravity == Tkright)
288837da2899SCharles.Forsyth 					ins = pins;
288937da2899SCharles.Forsyth 				else
289037da2899SCharles.Forsyth 					break;
289137da2899SCharles.Forsyth 			}
289237da2899SCharles.Forsyth 		}
289337da2899SCharles.Forsyth 	}
289437da2899SCharles.Forsyth 
289537da2899SCharles.Forsyth 	lmin = tktprevwrapline(tk, ins.line);
289637da2899SCharles.Forsyth 
289737da2899SCharles.Forsyth 	n = strlen(arg) + 1;
289837da2899SCharles.Forsyth 	if(n < Tkmaxitem)
289937da2899SCharles.Forsyth 		n = Tkmaxitem;
290037da2899SCharles.Forsyth 	tbuf = malloc(n);
290137da2899SCharles.Forsyth 	if(tbuf == nil)
290237da2899SCharles.Forsyth 		return TkNomem;
290337da2899SCharles.Forsyth 	buf = mallocz(Tkmaxitem, 0);
290437da2899SCharles.Forsyth 	if(buf == nil) {
290537da2899SCharles.Forsyth 		free(tbuf);
290637da2899SCharles.Forsyth 		return TkNomem;
290737da2899SCharles.Forsyth 	}
290837da2899SCharles.Forsyth 
290937da2899SCharles.Forsyth 	tagit = nil;
291037da2899SCharles.Forsyth 
291137da2899SCharles.Forsyth 	while(*arg != '\0') {
291237da2899SCharles.Forsyth 		arg = tkword(top, arg, tbuf, tbuf+n, nil);
291337da2899SCharles.Forsyth 		if(*arg != '\0') {
291437da2899SCharles.Forsyth 			/* tag list spec -- add some slop to tagextra for added tags */
291537da2899SCharles.Forsyth 			e = tktnewitem(TkTascii, (tkt->nexttag-1)/32 + 1, &tagit);
291637da2899SCharles.Forsyth 			if(e != nil) {
291737da2899SCharles.Forsyth 				free(tbuf);
291837da2899SCharles.Forsyth 				free(buf);
291937da2899SCharles.Forsyth 				return e;
292037da2899SCharles.Forsyth 			}
292137da2899SCharles.Forsyth 			arg = tkword(top, arg, buf, buf+Tkmaxitem, nil);
292237da2899SCharles.Forsyth 			p = buf;
292337da2899SCharles.Forsyth 			while(*p) {
292437da2899SCharles.Forsyth 				while(*p == ' ') {
292537da2899SCharles.Forsyth 					p++;
292637da2899SCharles.Forsyth 				}
292737da2899SCharles.Forsyth 				if(*p == '\0')
292837da2899SCharles.Forsyth 					break;
292937da2899SCharles.Forsyth 				pe = strchr(p, ' ');
293037da2899SCharles.Forsyth 				if(pe != nil)
293137da2899SCharles.Forsyth 					*pe = '\0';
293237da2899SCharles.Forsyth 				ti = tktfindtag(tkt->tags, p);
293337da2899SCharles.Forsyth 				if(ti == nil) {
293437da2899SCharles.Forsyth 					e = tktaddtaginfo(tk, p, &ti);
293537da2899SCharles.Forsyth 					if(e != nil) {
293637da2899SCharles.Forsyth 						if(tagit != nil)
293737da2899SCharles.Forsyth 							free(tagit);
293837da2899SCharles.Forsyth 						free(tbuf);
293937da2899SCharles.Forsyth 						free(buf);
294037da2899SCharles.Forsyth 						return e;
294137da2899SCharles.Forsyth 					}
294237da2899SCharles.Forsyth 				}
294337da2899SCharles.Forsyth 				tkttagbit(tagit, ti->id, 1);
294437da2899SCharles.Forsyth 				if(pe == nil)
294537da2899SCharles.Forsyth 					break;
294637da2899SCharles.Forsyth 				else
294737da2899SCharles.Forsyth 					p = pe+1;
294837da2899SCharles.Forsyth 			}
294937da2899SCharles.Forsyth 		}
295037da2899SCharles.Forsyth 		e = tktinsert(tk, &ins, tbuf, tagit);
295137da2899SCharles.Forsyth 		if(tagit != nil) {
295237da2899SCharles.Forsyth 			free(tagit);
295337da2899SCharles.Forsyth 			tagit = nil;
295437da2899SCharles.Forsyth 		}
295537da2899SCharles.Forsyth 		if(e != nil) {
295637da2899SCharles.Forsyth 			free(tbuf);
295737da2899SCharles.Forsyth 			free(buf);
295837da2899SCharles.Forsyth 			return e;
295937da2899SCharles.Forsyth 		}
296037da2899SCharles.Forsyth 	}
296137da2899SCharles.Forsyth 
296237da2899SCharles.Forsyth 	tktfixgeom(tk, lmin, ins.line, 0);
296337da2899SCharles.Forsyth 	tktextsize(tk, 1);
296437da2899SCharles.Forsyth 
296537da2899SCharles.Forsyth 	free(tbuf);
296637da2899SCharles.Forsyth 	free(buf);
296737da2899SCharles.Forsyth 
296837da2899SCharles.Forsyth 	return nil;
296937da2899SCharles.Forsyth }
297037da2899SCharles.Forsyth 
297137da2899SCharles.Forsyth static char*
tktextinserti(Tk * tk,char * arg,char ** val)297237da2899SCharles.Forsyth tktextinserti(Tk *tk, char *arg, char **val)
297337da2899SCharles.Forsyth {
297437da2899SCharles.Forsyth 	int n;
297537da2899SCharles.Forsyth 	TkTline *lmin;
297637da2899SCharles.Forsyth 	TkTindex ix, is1, is2;
297737da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
297837da2899SCharles.Forsyth 	char *tbuf, *buf;
297937da2899SCharles.Forsyth 
298037da2899SCharles.Forsyth 	USED(val);
298137da2899SCharles.Forsyth 
298237da2899SCharles.Forsyth 	if(tk->flag&Tkdisabled)
298337da2899SCharles.Forsyth 		return nil;
298437da2899SCharles.Forsyth 
298537da2899SCharles.Forsyth 	buf = mallocz(Tkmaxitem, 0);
298637da2899SCharles.Forsyth 	if(buf == nil)
298737da2899SCharles.Forsyth 		return TkNomem;
298837da2899SCharles.Forsyth 
298937da2899SCharles.Forsyth 	tbuf = nil;
299037da2899SCharles.Forsyth 	n = strlen(arg) + 1;
299137da2899SCharles.Forsyth 	if(n < Tkmaxitem)
299237da2899SCharles.Forsyth 		tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
299337da2899SCharles.Forsyth 	else {
299437da2899SCharles.Forsyth 		tbuf = malloc(n);
299537da2899SCharles.Forsyth 		if(tbuf == nil) {
299637da2899SCharles.Forsyth 			free(buf);
299737da2899SCharles.Forsyth 			return TkNomem;
299837da2899SCharles.Forsyth 		}
299937da2899SCharles.Forsyth 		tkword(tk->env->top, arg, tbuf, buf+n, nil);
300037da2899SCharles.Forsyth 	}
300137da2899SCharles.Forsyth 	if(*buf == '\0')
300237da2899SCharles.Forsyth 		goto Ret;
300337da2899SCharles.Forsyth 	if(!tktmarkind(tk, "insert", &ix)) {
300437da2899SCharles.Forsyth 		print("tktextinserti: botch\n");
300537da2899SCharles.Forsyth 		goto Ret;
300637da2899SCharles.Forsyth 	}
300737da2899SCharles.Forsyth 	if(tktgetsel(tk, &is1, &is2)) {
300837da2899SCharles.Forsyth 		if(tktindcompare(tkt, &is1, TkLte, &ix) &&
300937da2899SCharles.Forsyth 		   tktindcompare(tkt, &is2, TkGte, &ix)) {
301037da2899SCharles.Forsyth 			tktextdelete(tk, "sel.first sel.last", nil);
301137da2899SCharles.Forsyth 			/* delete might have changed ix item */
301237da2899SCharles.Forsyth 			tktmarkind(tk, "insert", &ix);
301337da2899SCharles.Forsyth 		}
301437da2899SCharles.Forsyth 	}
301537da2899SCharles.Forsyth 
301637da2899SCharles.Forsyth 	lmin = tktprevwrapline(tk, ix.line);
301737da2899SCharles.Forsyth 	tktinsert(tk, &ix, tbuf==nil ? buf : tbuf, 0);
301837da2899SCharles.Forsyth 	tktfixgeom(tk, lmin, ix.line, 0);
301937da2899SCharles.Forsyth 	if(tktmarkind(tk, "insert", &ix))		/* index doesn't remain valid after fixgeom */
302037da2899SCharles.Forsyth 		tktsee(tk, &ix, 0);
302137da2899SCharles.Forsyth 	tktextsize(tk, 1);
302237da2899SCharles.Forsyth Ret:
302337da2899SCharles.Forsyth 	if(tbuf != nil)
302437da2899SCharles.Forsyth 		free(tbuf);
302537da2899SCharles.Forsyth 	free(buf);
302637da2899SCharles.Forsyth 	return nil;
302737da2899SCharles.Forsyth }
302837da2899SCharles.Forsyth 
302937da2899SCharles.Forsyth static char*
tktextmark(Tk * tk,char * arg,char ** val)303037da2899SCharles.Forsyth tktextmark(Tk *tk, char *arg, char **val)
303137da2899SCharles.Forsyth {
303237da2899SCharles.Forsyth 	char *buf;
303337da2899SCharles.Forsyth 	TkCmdtab *cmd;
303437da2899SCharles.Forsyth 
303537da2899SCharles.Forsyth 	buf = mallocz(Tkmaxitem, 0);
303637da2899SCharles.Forsyth 	if(buf == nil)
303737da2899SCharles.Forsyth 		return TkNomem;
303837da2899SCharles.Forsyth 	arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
303937da2899SCharles.Forsyth 	for(cmd = tktmarkcmd; cmd->name != nil; cmd++) {
304037da2899SCharles.Forsyth 		if(strcmp(cmd->name, buf) == 0) {
304137da2899SCharles.Forsyth 			free(buf);
304237da2899SCharles.Forsyth 			return cmd->fn(tk, arg, val);
304337da2899SCharles.Forsyth 		}
304437da2899SCharles.Forsyth 	}
304537da2899SCharles.Forsyth 	free(buf);
304637da2899SCharles.Forsyth 	return TkBadcm;
304737da2899SCharles.Forsyth }
304837da2899SCharles.Forsyth 
304937da2899SCharles.Forsyth static char*
tktextscan(Tk * tk,char * arg,char ** val)305037da2899SCharles.Forsyth tktextscan(Tk *tk, char *arg, char **val)
305137da2899SCharles.Forsyth {
305237da2899SCharles.Forsyth 	char *e;
305337da2899SCharles.Forsyth 	int mark, x, y, xmax, ymax, vh, vw;
305437da2899SCharles.Forsyth 	Point p, odeltatv;
305537da2899SCharles.Forsyth 	char buf[Tkmaxitem];
305637da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
305737da2899SCharles.Forsyth 
305837da2899SCharles.Forsyth 	USED(val);
305937da2899SCharles.Forsyth 
306037da2899SCharles.Forsyth 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
306137da2899SCharles.Forsyth 
306237da2899SCharles.Forsyth 	if(strcmp(buf, "mark") == 0)
306337da2899SCharles.Forsyth 		mark = 1;
306437da2899SCharles.Forsyth 	else
306537da2899SCharles.Forsyth 	if(strcmp(buf, "dragto") == 0)
306637da2899SCharles.Forsyth 		mark = 0;
306737da2899SCharles.Forsyth 	else
306837da2899SCharles.Forsyth 		return TkBadcm;
306937da2899SCharles.Forsyth 
307037da2899SCharles.Forsyth 	e = tkxyparse(tk, &arg, &p);
307137da2899SCharles.Forsyth 	if(e != nil)
307237da2899SCharles.Forsyth 		return e;
307337da2899SCharles.Forsyth 
307437da2899SCharles.Forsyth 	if(mark)
307537da2899SCharles.Forsyth 		tkt->track = p;
307637da2899SCharles.Forsyth 	else {
307737da2899SCharles.Forsyth 		odeltatv = tkt->deltatv;
307837da2899SCharles.Forsyth 		vw = tk->act.width - tk->ipad.x;
307937da2899SCharles.Forsyth 		vh = tk->act.height - tk->ipad.y;
308037da2899SCharles.Forsyth 		ymax = tkt->end.prev->orig.y + tkt->end.prev->height - vh;
308137da2899SCharles.Forsyth 		y = tkt->deltatv.y -10*(p.y - tkt->track.y);
308237da2899SCharles.Forsyth 		if(y > ymax)
308337da2899SCharles.Forsyth 			y = ymax;
308437da2899SCharles.Forsyth 		if(y < 0)
308537da2899SCharles.Forsyth 			y = 0;
308637da2899SCharles.Forsyth 		tkt->deltatv.y = y;
308737da2899SCharles.Forsyth 		e = tktsetscroll(tk, Tkvertical);
308837da2899SCharles.Forsyth 		if(e != nil)
308937da2899SCharles.Forsyth 			return e;
309037da2899SCharles.Forsyth 		if(tkt->opts[TkTwrap] == Tkwrapnone) {
309137da2899SCharles.Forsyth 			xmax = tktmaxwid(tkt->start.next) - vw;
309237da2899SCharles.Forsyth 			x = tkt->deltatv.x - 10*(p.x - tkt->track.x);
309337da2899SCharles.Forsyth 			if(x > xmax)
309437da2899SCharles.Forsyth 				x = xmax;
309537da2899SCharles.Forsyth 			if(x < 0)
309637da2899SCharles.Forsyth 				x = 0;
309737da2899SCharles.Forsyth 			tkt->deltatv.x = x;
309837da2899SCharles.Forsyth 			e = tktsetscroll(tk, Tkhorizontal);
309937da2899SCharles.Forsyth 			if(e != nil)
310037da2899SCharles.Forsyth 				return e;
310137da2899SCharles.Forsyth 		}
310237da2899SCharles.Forsyth 		tktfixscroll(tk, odeltatv);
310337da2899SCharles.Forsyth 		tkt->track = p;
310437da2899SCharles.Forsyth 	}
310537da2899SCharles.Forsyth 
310637da2899SCharles.Forsyth 	return nil;
310737da2899SCharles.Forsyth }
310837da2899SCharles.Forsyth 
310937da2899SCharles.Forsyth static char*
tktextscrollpages(Tk * tk,char * arg,char ** val)311037da2899SCharles.Forsyth tktextscrollpages(Tk *tk, char *arg, char **val)
311137da2899SCharles.Forsyth {
311237da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
311337da2899SCharles.Forsyth 
311437da2899SCharles.Forsyth 	USED(tkt);
311537da2899SCharles.Forsyth 	USED(arg);
311637da2899SCharles.Forsyth 	USED(val);
311737da2899SCharles.Forsyth 	return nil;
311837da2899SCharles.Forsyth }
311937da2899SCharles.Forsyth 
312037da2899SCharles.Forsyth static char*
tktextsearch(Tk * tk,char * arg,char ** val)312137da2899SCharles.Forsyth tktextsearch(Tk *tk, char *arg, char **val)
312237da2899SCharles.Forsyth {
312337da2899SCharles.Forsyth 	int i, n;
312437da2899SCharles.Forsyth 	Rune r;
312537da2899SCharles.Forsyth 	char *e, *s;
312637da2899SCharles.Forsyth 	int wrap, fwd, nocase;
312737da2899SCharles.Forsyth 	TkText *tkt;
312837da2899SCharles.Forsyth 	TkTindex ix1, ix2, ixstart, ixend, tx;
312937da2899SCharles.Forsyth 	char buf[Tkmaxitem];
313037da2899SCharles.Forsyth 
313137da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
313237da2899SCharles.Forsyth 
313337da2899SCharles.Forsyth 	fwd = 1;
313437da2899SCharles.Forsyth 	nocase = 0;
313537da2899SCharles.Forsyth 
313637da2899SCharles.Forsyth 	while(*arg != '\0') {
313737da2899SCharles.Forsyth 		arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
313837da2899SCharles.Forsyth 		if(*buf != '-')
313937da2899SCharles.Forsyth 			break;
314037da2899SCharles.Forsyth 		if(strcmp(buf, "-backwards") == 0)
314137da2899SCharles.Forsyth 			fwd = 0;
314237da2899SCharles.Forsyth 		else if(strcmp(buf, "-nocase") == 0)
314337da2899SCharles.Forsyth 			nocase = 1;
314437da2899SCharles.Forsyth 		else if(strcmp(buf, "--") == 0) {
314537da2899SCharles.Forsyth 			arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
314637da2899SCharles.Forsyth 			break;
314737da2899SCharles.Forsyth 		}
314837da2899SCharles.Forsyth 	}
314937da2899SCharles.Forsyth 
315037da2899SCharles.Forsyth 	tktstartind(tkt, &ixstart);
315137da2899SCharles.Forsyth 	tktadjustind(tkt, TkTbycharstart, &ixstart);
315237da2899SCharles.Forsyth 	tktendind(tkt, &ixend);
315337da2899SCharles.Forsyth 
315437da2899SCharles.Forsyth 	if(*arg == '\0')
315537da2899SCharles.Forsyth 		return TkOparg;
315637da2899SCharles.Forsyth 
315737da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &ix1);
315837da2899SCharles.Forsyth 	if(e != nil)
315937da2899SCharles.Forsyth  		return e;
316037da2899SCharles.Forsyth 	tktadjustind(tkt, fwd? TkTbycharstart : TkTbycharback, &ix1);
316137da2899SCharles.Forsyth 
316237da2899SCharles.Forsyth 	if(*arg != '\0') {
316337da2899SCharles.Forsyth 		wrap = 0;
316437da2899SCharles.Forsyth 		e = tktindparse(tk, &arg, &ix2);
316537da2899SCharles.Forsyth 		if(e != nil)
316637da2899SCharles.Forsyth 			return e;
316737da2899SCharles.Forsyth 		if(!fwd)
316837da2899SCharles.Forsyth 			tktadjustind(tkt, TkTbycharback, &ix2);
316937da2899SCharles.Forsyth 	}
317037da2899SCharles.Forsyth 	else {
317137da2899SCharles.Forsyth 		wrap = 1;
317237da2899SCharles.Forsyth 		if(fwd) {
317337da2899SCharles.Forsyth 			if(tktindcompare(tkt, &ix1, TkEq, &ixstart))
317437da2899SCharles.Forsyth 				ix2 = ixend;
317537da2899SCharles.Forsyth 			else {
317637da2899SCharles.Forsyth 				ix2 = ix1;
317737da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbycharback, &ix2);
317837da2899SCharles.Forsyth 			}
317937da2899SCharles.Forsyth 		}
318037da2899SCharles.Forsyth 		else {
318137da2899SCharles.Forsyth 			if(tktindcompare(tkt, &ix1, TkEq, &ixend))
318237da2899SCharles.Forsyth 				ix2 = ixstart;
318337da2899SCharles.Forsyth 			else {
318437da2899SCharles.Forsyth 				ix2 = ix1;
318537da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbychar, &ix2);
318637da2899SCharles.Forsyth 			}
318737da2899SCharles.Forsyth 		}
318837da2899SCharles.Forsyth 	}
318937da2899SCharles.Forsyth 	tktadjustind(tkt, TkTbycharstart, &ix2);
319037da2899SCharles.Forsyth 	if(tktindcompare(tkt, &ix1, TkEq, &ix2))
319137da2899SCharles.Forsyth 		return nil;
319237da2899SCharles.Forsyth 
319337da2899SCharles.Forsyth 	if(*buf == '\0')
319437da2899SCharles.Forsyth 		return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix1), tktlinepos(tkt, &ix1));
319537da2899SCharles.Forsyth 
319637da2899SCharles.Forsyth 	while(!(ix1.item == ix2.item && ix1.pos == ix2.pos)) {
319737da2899SCharles.Forsyth 		tx = ix1;
319837da2899SCharles.Forsyth 		for(i = 0; buf[i] != '\0'; i++) {
319937da2899SCharles.Forsyth 			switch(tx.item->kind) {
320037da2899SCharles.Forsyth 			case TkTascii:
320137da2899SCharles.Forsyth 				if(!tktcmatch(tx.item->istring[tx.pos], buf[i], nocase))
320237da2899SCharles.Forsyth 					goto nomatch;
320337da2899SCharles.Forsyth 				break;
320437da2899SCharles.Forsyth 			case TkTrune:
320537da2899SCharles.Forsyth 				s = tx.item->istring;
320637da2899SCharles.Forsyth 				s += tktutfpos(s, tx.pos);
320737da2899SCharles.Forsyth 				n = chartorune(&r, s);
320837da2899SCharles.Forsyth 				if(strncmp(s, buf+i, n) != 0)
320937da2899SCharles.Forsyth 					goto nomatch;
321037da2899SCharles.Forsyth 				i += n-1;
321137da2899SCharles.Forsyth 				break;
321237da2899SCharles.Forsyth 			case TkTtab:
321337da2899SCharles.Forsyth 				if(buf[i] != '\t')
321437da2899SCharles.Forsyth 					goto nomatch;
321537da2899SCharles.Forsyth 				break;
321637da2899SCharles.Forsyth 			case TkTnewline:
321737da2899SCharles.Forsyth 				if(buf[i] != '\n')
321837da2899SCharles.Forsyth 					goto nomatch;
321937da2899SCharles.Forsyth 				break;
322037da2899SCharles.Forsyth 			default:
322137da2899SCharles.Forsyth 				goto nomatch;
322237da2899SCharles.Forsyth 			}
322337da2899SCharles.Forsyth 			tktadjustind(tkt, TkTbychar, &tx);
322437da2899SCharles.Forsyth 		}
322537da2899SCharles.Forsyth 		return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix1), tktlinepos(tkt, &ix1));
322637da2899SCharles.Forsyth 	nomatch:
322737da2899SCharles.Forsyth 		if(fwd) {
322837da2899SCharles.Forsyth 			if(!tktadjustind(tkt, TkTbychar, &ix1)) {
322937da2899SCharles.Forsyth 				if(!wrap)
323037da2899SCharles.Forsyth 					break;
323137da2899SCharles.Forsyth 				ix1 = ixstart;
323237da2899SCharles.Forsyth 			}
323337da2899SCharles.Forsyth 		}
323437da2899SCharles.Forsyth 		else {
323537da2899SCharles.Forsyth 			if(!tktadjustind(tkt, TkTbycharback, &ix1)) {
323637da2899SCharles.Forsyth 				if(!wrap)
323737da2899SCharles.Forsyth 					break;
323837da2899SCharles.Forsyth 				ix1 = ixend;
323937da2899SCharles.Forsyth 			}
324037da2899SCharles.Forsyth 		}
324137da2899SCharles.Forsyth 	}
324237da2899SCharles.Forsyth 
324337da2899SCharles.Forsyth 	return nil;
324437da2899SCharles.Forsyth }
324537da2899SCharles.Forsyth 
324637da2899SCharles.Forsyth char*
tktextselection(Tk * tk,char * arg,char ** val)324737da2899SCharles.Forsyth tktextselection(Tk *tk, char *arg, char **val)
324837da2899SCharles.Forsyth {
324937da2899SCharles.Forsyth 	USED(val);
325037da2899SCharles.Forsyth 	if (strcmp(arg, " clear") == 0) {
325137da2899SCharles.Forsyth 		tktclearsel(tk);
325237da2899SCharles.Forsyth 		return nil;
325337da2899SCharles.Forsyth 	}
325437da2899SCharles.Forsyth 	else
325537da2899SCharles.Forsyth 		return TkBadcm;
325637da2899SCharles.Forsyth }
325737da2899SCharles.Forsyth 
325837da2899SCharles.Forsyth static void
doselectto(Tk * tk,Point p,int dbl)325937da2899SCharles.Forsyth doselectto(Tk *tk, Point p, int dbl)
326037da2899SCharles.Forsyth {
326137da2899SCharles.Forsyth 	int halfway;
326237da2899SCharles.Forsyth 	TkTindex cur, insert, first, last;
326337da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
326437da2899SCharles.Forsyth 	tktclearsel(tk);
326537da2899SCharles.Forsyth 
326637da2899SCharles.Forsyth 	halfway = tktxyind(tk, p.x, p.y, &cur);
326737da2899SCharles.Forsyth 
326837da2899SCharles.Forsyth 	if(!dbl) {
326937da2899SCharles.Forsyth 		if(!tktmarkind(tk, "insert", &insert))
327037da2899SCharles.Forsyth 			insert = cur;
327137da2899SCharles.Forsyth 
327237da2899SCharles.Forsyth 		if(tktindcompare(tkt, &cur, TkLt, &insert)) {
327337da2899SCharles.Forsyth 			first = cur;
327437da2899SCharles.Forsyth 			last = insert;
327537da2899SCharles.Forsyth 		}
327637da2899SCharles.Forsyth 		else {
327737da2899SCharles.Forsyth 			first = insert;
327837da2899SCharles.Forsyth 			last = cur;
327937da2899SCharles.Forsyth 			if(halfway)
328037da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbychar, &last);
328137da2899SCharles.Forsyth 			if(last.line == &tkt->end)
328237da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbycharback, &last);
328337da2899SCharles.Forsyth 			if(tktindcompare(tkt, &first, TkGte, &last))
328437da2899SCharles.Forsyth 				return;
328537da2899SCharles.Forsyth 			cur = last;
328637da2899SCharles.Forsyth 		}
328737da2899SCharles.Forsyth 		tktsee(tk, &cur, 0);
328837da2899SCharles.Forsyth 	}
328937da2899SCharles.Forsyth 	else {
329037da2899SCharles.Forsyth 		first = cur;
329137da2899SCharles.Forsyth 		last = cur;
329237da2899SCharles.Forsyth 		tktdoubleclick(tkt, &first, &last);
329337da2899SCharles.Forsyth 	}
329437da2899SCharles.Forsyth 
329537da2899SCharles.Forsyth 	tkttagchange(tk, TkTselid, &first, &last, 1);
329637da2899SCharles.Forsyth }
329737da2899SCharles.Forsyth 
329837da2899SCharles.Forsyth static void
autoselect(Tk * tk,void * v,int cancelled)329937da2899SCharles.Forsyth autoselect(Tk *tk, void *v, int cancelled)
330037da2899SCharles.Forsyth {
330137da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
330237da2899SCharles.Forsyth 	Rectangle hitr;
330337da2899SCharles.Forsyth 	Point p;
330437da2899SCharles.Forsyth 	USED(v);
330537da2899SCharles.Forsyth 
330637da2899SCharles.Forsyth 	if (cancelled)
330737da2899SCharles.Forsyth 		return;
330837da2899SCharles.Forsyth 
330937da2899SCharles.Forsyth 	p = scr2local(tk, tkt->track);
331037da2899SCharles.Forsyth 	if (tkvisiblerect(tk, &hitr) && ptinrect(p, hitr))
331137da2899SCharles.Forsyth 		return;
331237da2899SCharles.Forsyth 	doselectto(tk, p, 0);
331337da2899SCharles.Forsyth 	tkdirty(tk);
331437da2899SCharles.Forsyth 	tkupdate(tk->env->top);
331537da2899SCharles.Forsyth }
331637da2899SCharles.Forsyth 
331737da2899SCharles.Forsyth static char*
tktextselectto(Tk * tk,char * arg,char ** val)331837da2899SCharles.Forsyth tktextselectto(Tk *tk, char *arg, char **val)
331937da2899SCharles.Forsyth {
332037da2899SCharles.Forsyth 	int dbl;
332137da2899SCharles.Forsyth 	char *e;
332237da2899SCharles.Forsyth 	Point p;
332337da2899SCharles.Forsyth 	Rectangle hitr;
332437da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
332537da2899SCharles.Forsyth 
332637da2899SCharles.Forsyth 	USED(val);
332737da2899SCharles.Forsyth 
332837da2899SCharles.Forsyth 	if(tkt->tflag & (TkTjustfoc|TkTnodrag))
332937da2899SCharles.Forsyth 		return nil;
333037da2899SCharles.Forsyth 
333137da2899SCharles.Forsyth 	e = tkxyparse(tk, &arg, &p);
333237da2899SCharles.Forsyth 	if(e != nil)
333337da2899SCharles.Forsyth 		return e;
333437da2899SCharles.Forsyth 	tkt->track = p;
333537da2899SCharles.Forsyth 	p = scr2local(tk, p);
333637da2899SCharles.Forsyth 
333737da2899SCharles.Forsyth 	arg = tkskip(arg, " ");
333837da2899SCharles.Forsyth 	if(*arg == 'd') {
333937da2899SCharles.Forsyth 		tkcancelrepeat(tk);
334037da2899SCharles.Forsyth 		dbl = 1;
334137da2899SCharles.Forsyth 		tkt->tflag |= TkTnodrag;
334237da2899SCharles.Forsyth 	} else {
334337da2899SCharles.Forsyth 		dbl = 0;
334437da2899SCharles.Forsyth 		if (!tkvisiblerect(tk, &hitr) || !ptinrect(p, hitr))
334537da2899SCharles.Forsyth 			return nil;
334637da2899SCharles.Forsyth 	}
334737da2899SCharles.Forsyth 	doselectto(tk, p, dbl);
334837da2899SCharles.Forsyth 	return nil;
334937da2899SCharles.Forsyth }
335037da2899SCharles.Forsyth 
335137da2899SCharles.Forsyth static char tktleft1[] = "{[(<";
335237da2899SCharles.Forsyth static char tktright1[] = "}])>";
335337da2899SCharles.Forsyth static char tktleft2[] = "\n";
335437da2899SCharles.Forsyth static char tktleft3[] = "\'\"`";
335537da2899SCharles.Forsyth 
335637da2899SCharles.Forsyth static char *tktleft[] = {tktleft1, tktleft2, tktleft3, nil};
335737da2899SCharles.Forsyth static char *tktright[] = {tktright1,  tktleft2, tktleft3, nil};
335837da2899SCharles.Forsyth 
335937da2899SCharles.Forsyth static void
tktdoubleclick(TkText * tkt,TkTindex * first,TkTindex * last)336037da2899SCharles.Forsyth tktdoubleclick(TkText *tkt, TkTindex *first, TkTindex *last)
336137da2899SCharles.Forsyth {
336237da2899SCharles.Forsyth 	int c, i;
336337da2899SCharles.Forsyth 	TkTindex ix, ix2;
336437da2899SCharles.Forsyth 	char *r, *l, *p;
336537da2899SCharles.Forsyth 
336637da2899SCharles.Forsyth 	for(i = 0; tktleft[i] != nil; i++) {
336737da2899SCharles.Forsyth 		ix = *first;
336837da2899SCharles.Forsyth 		l = tktleft[i];
336937da2899SCharles.Forsyth 		r = tktright[i];
337037da2899SCharles.Forsyth 		/* try matching character to left, looking right */
337137da2899SCharles.Forsyth 		ix2 = ix;
337237da2899SCharles.Forsyth 		if(!tktadjustind(tkt, TkTbycharback, &ix2))
337337da2899SCharles.Forsyth 			c = '\n';
337437da2899SCharles.Forsyth 		else
337537da2899SCharles.Forsyth 			c = tktindrune(&ix2);
337637da2899SCharles.Forsyth 		p = strchr(l, c);
337737da2899SCharles.Forsyth 		if(p != nil) {
337837da2899SCharles.Forsyth 			if(tktclickmatch(tkt, c, r[p-l], 1, &ix)) {
337937da2899SCharles.Forsyth 				*last = ix;
338037da2899SCharles.Forsyth 				if(c != '\n')
338137da2899SCharles.Forsyth 					tktadjustind(tkt, TkTbycharback, last);
338237da2899SCharles.Forsyth 			}
338337da2899SCharles.Forsyth 			return;
338437da2899SCharles.Forsyth 		}
338537da2899SCharles.Forsyth 		/* try matching character to right, looking left */
338637da2899SCharles.Forsyth 		c = tktindrune(&ix);
338737da2899SCharles.Forsyth 		p = strchr(r, c);
338837da2899SCharles.Forsyth 		if(p != nil) {
338937da2899SCharles.Forsyth 			if(tktclickmatch(tkt, c, l[p-r], -1, &ix)) {
339037da2899SCharles.Forsyth 				*last = *first;
339137da2899SCharles.Forsyth 				if(c == '\n')
339237da2899SCharles.Forsyth 					tktadjustind(tkt, TkTbychar, last);
339337da2899SCharles.Forsyth 				*first = ix;
339437da2899SCharles.Forsyth 				if(!(c=='\n' && ix.line == tkt->start.next && ix.item == ix.line->items))
339537da2899SCharles.Forsyth 					tktadjustind(tkt, TkTbychar, first);
339637da2899SCharles.Forsyth 			}
339737da2899SCharles.Forsyth 			return;
339837da2899SCharles.Forsyth 		}
339937da2899SCharles.Forsyth 	}
340037da2899SCharles.Forsyth 	/* try filling out word to right */
340137da2899SCharles.Forsyth 	while(tkiswordchar(tktindrune(last))) {
340237da2899SCharles.Forsyth 		if(!tktadjustind(tkt, TkTbychar, last))
340337da2899SCharles.Forsyth 			break;
340437da2899SCharles.Forsyth 	}
340537da2899SCharles.Forsyth 	/* try filling out word to left */
340637da2899SCharles.Forsyth 	for(;;) {
340737da2899SCharles.Forsyth 		ix = *first;
340837da2899SCharles.Forsyth 		if(!tktadjustind(tkt, TkTbycharback, &ix))
340937da2899SCharles.Forsyth 			break;
341037da2899SCharles.Forsyth 		if(!tkiswordchar(tktindrune(&ix)))
341137da2899SCharles.Forsyth 			break;
341237da2899SCharles.Forsyth 		*first = ix;
341337da2899SCharles.Forsyth 	}
341437da2899SCharles.Forsyth }
341537da2899SCharles.Forsyth 
341637da2899SCharles.Forsyth static int
tktclickmatch(TkText * tkt,int cl,int cr,int dir,TkTindex * ix)341737da2899SCharles.Forsyth tktclickmatch(TkText *tkt, int cl, int cr, int dir, TkTindex *ix)
341837da2899SCharles.Forsyth {
341937da2899SCharles.Forsyth 	int c, nest, atend;
342037da2899SCharles.Forsyth 
342137da2899SCharles.Forsyth 	nest = 1;
342237da2899SCharles.Forsyth 	atend = 0;
342337da2899SCharles.Forsyth 	for(;;) {
342437da2899SCharles.Forsyth 		if(dir > 0) {
342537da2899SCharles.Forsyth 			if(atend)
342637da2899SCharles.Forsyth 				break;
342737da2899SCharles.Forsyth 			c = tktindrune(ix);
342837da2899SCharles.Forsyth 			atend = !tktadjustind(tkt, TkTbychar, ix);
342937da2899SCharles.Forsyth 		} else {
343037da2899SCharles.Forsyth 			if(!tktadjustind(tkt, TkTbycharback, ix))
343137da2899SCharles.Forsyth 				break;
343237da2899SCharles.Forsyth 			c = tktindrune(ix);
343337da2899SCharles.Forsyth 		}
343437da2899SCharles.Forsyth 		if(c == cr){
343537da2899SCharles.Forsyth 			if(--nest==0)
343637da2899SCharles.Forsyth 				return 1;
343737da2899SCharles.Forsyth 		}else if(c == cl)
343837da2899SCharles.Forsyth 			nest++;
343937da2899SCharles.Forsyth 	}
344037da2899SCharles.Forsyth 	return cl=='\n' && nest==1;
344137da2899SCharles.Forsyth }
344237da2899SCharles.Forsyth 
344337da2899SCharles.Forsyth /*
344437da2899SCharles.Forsyth  * return the line before line l, unless word wrap is on,
344537da2899SCharles.Forsyth  * (for the first word of line l), in which case return the last non-empty line before that.
344637da2899SCharles.Forsyth  * tktgeom might then combine the end of that line with the start of the insertion
344737da2899SCharles.Forsyth  * (unless there is a newline in the way).
344837da2899SCharles.Forsyth  */
344937da2899SCharles.Forsyth TkTline*
tktprevwrapline(Tk * tk,TkTline * l)345037da2899SCharles.Forsyth tktprevwrapline(Tk *tk, TkTline *l)
345137da2899SCharles.Forsyth {
345237da2899SCharles.Forsyth 	TkTitem *i;
345337da2899SCharles.Forsyth 	int *opts, wrapmode;
345437da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
345537da2899SCharles.Forsyth 	TkEnv env;
345637da2899SCharles.Forsyth 
345737da2899SCharles.Forsyth 	if(l == nil)
345837da2899SCharles.Forsyth 		return nil;
345937da2899SCharles.Forsyth 	/* some spacing depends on tags of first non-mark on display line */
346037da2899SCharles.Forsyth 	for(i = l->items; i != nil; i = i->next)
346137da2899SCharles.Forsyth 		if(i->kind != TkTmark && i->kind != TkTcontline)
346237da2899SCharles.Forsyth 			break;
346337da2899SCharles.Forsyth 	if(i == nil || i->kind == TkTnewline)	/* can't use !tkanytags(i) because it doesn't check env */
346437da2899SCharles.Forsyth 		return l->prev;
346537da2899SCharles.Forsyth 	opts = mallocz(TkTnumopts*sizeof(int), 0);
346637da2899SCharles.Forsyth 	if(opts == nil)
346737da2899SCharles.Forsyth 		return l->prev;	/* in worst case gets word wrap wrong */
346837da2899SCharles.Forsyth 	tkttagopts(tk, i, opts, &env, nil, 1);
346937da2899SCharles.Forsyth 	wrapmode = opts[TkTwrap];
347037da2899SCharles.Forsyth 	free(opts);
347137da2899SCharles.Forsyth 	if(wrapmode != Tkwrapword)
347237da2899SCharles.Forsyth 		return l->prev;
347337da2899SCharles.Forsyth 	if(l->prev != &tkt->start)
347437da2899SCharles.Forsyth 		l = l->prev;	/* having been processed by tktgeom, shouldn't have extraneous marks etc */
347537da2899SCharles.Forsyth 	return l->prev;
347637da2899SCharles.Forsyth }
347737da2899SCharles.Forsyth 
347837da2899SCharles.Forsyth static char*
tktextsetcursor(Tk * tk,char * arg,char ** val)347937da2899SCharles.Forsyth tktextsetcursor(Tk *tk, char *arg, char **val)
348037da2899SCharles.Forsyth {
348137da2899SCharles.Forsyth 	char *e;
348237da2899SCharles.Forsyth 	TkTindex ix;
348337da2899SCharles.Forsyth 	TkTmarkinfo *mi;
348437da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
348537da2899SCharles.Forsyth 
348637da2899SCharles.Forsyth 	USED(val);
348737da2899SCharles.Forsyth 
348837da2899SCharles.Forsyth 	/* do clearsel here, because it can change indices */
348937da2899SCharles.Forsyth 	tktclearsel(tk);
349037da2899SCharles.Forsyth 
349137da2899SCharles.Forsyth 	e = tktindparse(tk, &arg, &ix);
349237da2899SCharles.Forsyth 	if(e != nil)
349337da2899SCharles.Forsyth 		return e;
349437da2899SCharles.Forsyth 
349537da2899SCharles.Forsyth 	mi = tktfindmark(tkt->marks, "insert");
349637da2899SCharles.Forsyth 	if(tktdbg && mi == nil) {
349737da2899SCharles.Forsyth 		print("tktextsetcursor: botch\n");
349837da2899SCharles.Forsyth 		return nil;
349937da2899SCharles.Forsyth 	}
350037da2899SCharles.Forsyth 	tktmarkmove(tk, mi, &ix);
350137da2899SCharles.Forsyth 	tktsee(tk, &ix, 0);
350237da2899SCharles.Forsyth 	return nil;
350337da2899SCharles.Forsyth }
350437da2899SCharles.Forsyth 
350537da2899SCharles.Forsyth static char*
tktexttag(Tk * tk,char * arg,char ** val)350637da2899SCharles.Forsyth tktexttag(Tk *tk, char *arg, char **val)
350737da2899SCharles.Forsyth {
350837da2899SCharles.Forsyth 	char *buf;
350937da2899SCharles.Forsyth 	TkCmdtab *cmd;
351037da2899SCharles.Forsyth 
351137da2899SCharles.Forsyth 	buf = mallocz(Tkmaxitem, 0);
351237da2899SCharles.Forsyth 	if(buf == nil)
351337da2899SCharles.Forsyth 		return TkNomem;
351437da2899SCharles.Forsyth 	arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
351537da2899SCharles.Forsyth 	for(cmd = tkttagcmd; cmd->name != nil; cmd++) {
351637da2899SCharles.Forsyth 		if(strcmp(cmd->name, buf) == 0) {
351737da2899SCharles.Forsyth 			free(buf);
351837da2899SCharles.Forsyth 			return cmd->fn(tk, arg, val);
351937da2899SCharles.Forsyth 		}
352037da2899SCharles.Forsyth 	}
352137da2899SCharles.Forsyth 	free(buf);
352237da2899SCharles.Forsyth 	return TkBadcm;
352337da2899SCharles.Forsyth }
352437da2899SCharles.Forsyth 
352537da2899SCharles.Forsyth static char*
tktextwindow(Tk * tk,char * arg,char ** val)352637da2899SCharles.Forsyth tktextwindow(Tk *tk, char *arg, char **val)
352737da2899SCharles.Forsyth {
352837da2899SCharles.Forsyth 	char buf[Tkmaxitem];
352937da2899SCharles.Forsyth 	TkCmdtab *cmd;
353037da2899SCharles.Forsyth 
353137da2899SCharles.Forsyth 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
353237da2899SCharles.Forsyth 	for(cmd = tktwincmd; cmd->name != nil; cmd++) {
353337da2899SCharles.Forsyth 		if(strcmp(cmd->name, buf) == 0)
353437da2899SCharles.Forsyth 			return cmd->fn(tk, arg, val);
353537da2899SCharles.Forsyth 	}
353637da2899SCharles.Forsyth 	return TkBadcm;
353737da2899SCharles.Forsyth }
353837da2899SCharles.Forsyth 
353937da2899SCharles.Forsyth static char*
tktextxview(Tk * tk,char * arg,char ** val)354037da2899SCharles.Forsyth tktextxview(Tk *tk, char *arg, char **val)
354137da2899SCharles.Forsyth {
354237da2899SCharles.Forsyth 	int ntot, vw;
354337da2899SCharles.Forsyth 	char *e;
354437da2899SCharles.Forsyth 	Point odeltatv;
354537da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
354637da2899SCharles.Forsyth 
354737da2899SCharles.Forsyth 	odeltatv = tkt->deltatv;
354837da2899SCharles.Forsyth 	vw = tk->act.width - tk->ipad.x;
354937da2899SCharles.Forsyth 	ntot = tktmaxwid(tkt->start.next);
355037da2899SCharles.Forsyth 	if(ntot < tkt->deltatv.x +vw)
355137da2899SCharles.Forsyth 		ntot = tkt->deltatv.x + vw;
355237da2899SCharles.Forsyth 	e = tktview(tk, arg, val, vw, &tkt->deltatv.x, ntot, Tkhorizontal);
355337da2899SCharles.Forsyth 	if(e == nil) {
355437da2899SCharles.Forsyth 		e = tktsetscroll(tk, Tkhorizontal);
355537da2899SCharles.Forsyth 		if(e == nil)
355637da2899SCharles.Forsyth 			tktfixscroll(tk, odeltatv);
355737da2899SCharles.Forsyth 	}
355837da2899SCharles.Forsyth 	return e;
355937da2899SCharles.Forsyth }
356037da2899SCharles.Forsyth 
356137da2899SCharles.Forsyth static int
istext(TkTline * l)356237da2899SCharles.Forsyth istext(TkTline *l)
356337da2899SCharles.Forsyth {
356437da2899SCharles.Forsyth 	TkTitem *i;
356537da2899SCharles.Forsyth 
356637da2899SCharles.Forsyth 	for(i = l->items; i != nil; i = i->next)
356737da2899SCharles.Forsyth 		if(i->kind == TkTwin || i->kind == TkTmark)
356837da2899SCharles.Forsyth 			return 0;
356937da2899SCharles.Forsyth 	return 1;
357037da2899SCharles.Forsyth }
357137da2899SCharles.Forsyth 
357237da2899SCharles.Forsyth static void
tkadjpage(Tk * tk,int ody,int * dy)357337da2899SCharles.Forsyth tkadjpage(Tk *tk, int ody, int *dy)
357437da2899SCharles.Forsyth {
357537da2899SCharles.Forsyth 	int y, a, b, d;
357637da2899SCharles.Forsyth 	TkTindex ix;
357737da2899SCharles.Forsyth 	TkTline *l;
357837da2899SCharles.Forsyth 
357937da2899SCharles.Forsyth 	d = *dy-ody;
358037da2899SCharles.Forsyth 	y = d > 0 ? tk->act.height : 0;
358137da2899SCharles.Forsyth 	tktxyind(tk, 0, y-d, &ix);
358237da2899SCharles.Forsyth 	if((l = ix.line) != nil && istext(l)){
358337da2899SCharles.Forsyth 		a = l->orig.y;
358437da2899SCharles.Forsyth 		b = a+l->height;
358537da2899SCharles.Forsyth /* print("AP: %d %d %d (%d+%d)\n", a, ody+y, b, ody, y); */
358637da2899SCharles.Forsyth 		if(a+2 < ody+y && ody+y < b-2){	/* partially obscured line */
358737da2899SCharles.Forsyth 			if(d > 0)
358837da2899SCharles.Forsyth 				*dy -= ody+y-a;
358937da2899SCharles.Forsyth 			else
359037da2899SCharles.Forsyth 				*dy += b-ody;
359137da2899SCharles.Forsyth 		}
359237da2899SCharles.Forsyth 	}
359337da2899SCharles.Forsyth }
359437da2899SCharles.Forsyth 
359537da2899SCharles.Forsyth static char*
tktextyview(Tk * tk,char * arg,char ** val)359637da2899SCharles.Forsyth tktextyview(Tk *tk, char *arg, char **val)
359737da2899SCharles.Forsyth {
359837da2899SCharles.Forsyth 	int ntot, vh, d;
359937da2899SCharles.Forsyth 	char *e;
360037da2899SCharles.Forsyth 	TkTline *l;
360137da2899SCharles.Forsyth 	Point odeltatv;
360237da2899SCharles.Forsyth 	TkTindex ix;
360337da2899SCharles.Forsyth 	TkText *tkt = TKobj(TkText, tk);
360437da2899SCharles.Forsyth 	char buf[Tkmaxitem], *v;
360537da2899SCharles.Forsyth 
360637da2899SCharles.Forsyth 	if(*arg != '\0') {
360737da2899SCharles.Forsyth 		v = tkitem(buf, arg);
360837da2899SCharles.Forsyth 		if(strcmp(buf, "-pickplace") == 0)
360937da2899SCharles.Forsyth 			return tktextsee(tk,v, val);
361037da2899SCharles.Forsyth 		if(strcmp(buf, "moveto") != 0 && strcmp(buf, "scroll") != 0) {
361137da2899SCharles.Forsyth 			e = tktindparse(tk, &arg, &ix);
361237da2899SCharles.Forsyth 			if(e != nil)
361337da2899SCharles.Forsyth 				return e;
361437da2899SCharles.Forsyth 			tktsee(tk, &ix, 1);
361537da2899SCharles.Forsyth 			return nil;
361637da2899SCharles.Forsyth 		}
361737da2899SCharles.Forsyth 	}
361837da2899SCharles.Forsyth 	odeltatv = tkt->deltatv;
361937da2899SCharles.Forsyth 	vh = tk->act.height;
362037da2899SCharles.Forsyth 	l =  tkt->end.prev;
362137da2899SCharles.Forsyth 	ntot = l->orig.y + l->height;
362237da2899SCharles.Forsyth //	if(ntot < tkt->deltatv.y + vh)
362337da2899SCharles.Forsyth //		ntot = tkt->deltatv.y + vh;
362437da2899SCharles.Forsyth 	e = tktview(tk, arg, val, vh, &tkt->deltatv.y, ntot, Tkvertical);
362537da2899SCharles.Forsyth 	d = tkt->deltatv.y-odeltatv.y;
362637da2899SCharles.Forsyth 	if(d == vh || d == -vh)
362737da2899SCharles.Forsyth 		tkadjpage(tk, odeltatv.y, &tkt->deltatv.y);
362837da2899SCharles.Forsyth 	if(e == nil) {
362937da2899SCharles.Forsyth 		e = tktsetscroll(tk, Tkvertical);
363037da2899SCharles.Forsyth 		if(e == nil)
363137da2899SCharles.Forsyth 			tktfixscroll(tk, odeltatv);
363237da2899SCharles.Forsyth 	}
363337da2899SCharles.Forsyth 	return e;
363437da2899SCharles.Forsyth }
363537da2899SCharles.Forsyth static void
tktextfocusorder(Tk * tk)363637da2899SCharles.Forsyth tktextfocusorder(Tk *tk)
363737da2899SCharles.Forsyth {
363837da2899SCharles.Forsyth 	TkTindex ix;
363937da2899SCharles.Forsyth 	TkText *t;
364037da2899SCharles.Forsyth 	Tk *isub;
364137da2899SCharles.Forsyth 
364237da2899SCharles.Forsyth 	t = TKobj(TkText, tk);
364337da2899SCharles.Forsyth 	tktstartind(t, &ix);
364437da2899SCharles.Forsyth 	do {
364537da2899SCharles.Forsyth 		if(ix.item->kind == TkTwin) {
364637da2899SCharles.Forsyth 			isub = ix.item->iwin->sub;
364737da2899SCharles.Forsyth 			if(isub != nil)
364837da2899SCharles.Forsyth 				tkappendfocusorder(isub);
364937da2899SCharles.Forsyth 		}
365037da2899SCharles.Forsyth 	} while(tktadjustind(t, TkTbyitem, &ix));
365137da2899SCharles.Forsyth }
365237da2899SCharles.Forsyth 
365337da2899SCharles.Forsyth TkCmdtab tktextcmd[] =
365437da2899SCharles.Forsyth {
365537da2899SCharles.Forsyth 	"bbox",			tktextbbox,
365637da2899SCharles.Forsyth 	"cget",			tktextcget,
365737da2899SCharles.Forsyth 	"compare",		tktextcompare,
365837da2899SCharles.Forsyth 	"configure",		tktextconfigure,
365937da2899SCharles.Forsyth 	"debug",		tktextdebug,
366037da2899SCharles.Forsyth 	"delete",		tktextdelete,
366137da2899SCharles.Forsyth 	"dlineinfo",		tktextdlineinfo,
366237da2899SCharles.Forsyth 	"dump",			tktextdump,
366337da2899SCharles.Forsyth 	"get",			tktextget,
366437da2899SCharles.Forsyth 	"index",		tktextindex,
366537da2899SCharles.Forsyth 	"insert",		tktextinsert,
366637da2899SCharles.Forsyth 	"mark",			tktextmark,
366737da2899SCharles.Forsyth 	"scan",			tktextscan,
366837da2899SCharles.Forsyth 	"search",		tktextsearch,
366937da2899SCharles.Forsyth 	"see",			tktextsee,
367037da2899SCharles.Forsyth 	"selection",		tktextselection,
367137da2899SCharles.Forsyth 	"tag",			tktexttag,
367237da2899SCharles.Forsyth 	"window",		tktextwindow,
367337da2899SCharles.Forsyth 	"xview",		tktextxview,
367437da2899SCharles.Forsyth 	"yview",		tktextyview,
367537da2899SCharles.Forsyth 	"tkTextButton1",	tktextbutton1,
367637da2899SCharles.Forsyth 	"tkTextButton1R",	tktextbutton1r,
367737da2899SCharles.Forsyth 	"tkTextDelIns",		tktextdelins,
367837da2899SCharles.Forsyth 	"tkTextInsert",		tktextinserti,
367937da2899SCharles.Forsyth 	"tkTextSelectTo",	tktextselectto,
368037da2899SCharles.Forsyth 	"tkTextSetCursor",	tktextsetcursor,
368137da2899SCharles.Forsyth 	"tkTextScrollPages",	tktextscrollpages,
368237da2899SCharles.Forsyth 	"tkTextCursor",		tktextcursor,
368337da2899SCharles.Forsyth 	nil
368437da2899SCharles.Forsyth };
368537da2899SCharles.Forsyth 
368637da2899SCharles.Forsyth TkMethod textmethod = {
368737da2899SCharles.Forsyth 	"text",
368837da2899SCharles.Forsyth 	tktextcmd,
368937da2899SCharles.Forsyth 	tkfreetext,
369037da2899SCharles.Forsyth 	tkdrawtext,
369137da2899SCharles.Forsyth 	tktextgeom,
369237da2899SCharles.Forsyth 	nil,
369337da2899SCharles.Forsyth 	tktextfocusorder,
369437da2899SCharles.Forsyth 	tktdirty,
369537da2899SCharles.Forsyth 	tktrelpos,
369637da2899SCharles.Forsyth 	tktextevent,
369737da2899SCharles.Forsyth 	nil,				/* XXX need to implement textsee */
3698c9c0d12eSforsyth 	tktinwindow,
3699c9c0d12eSforsyth 	nil,
3700c9c0d12eSforsyth 	tktxtforgetsub,
370137da2899SCharles.Forsyth };
3702