Charles.Forsyth20061220965a0ca19 years agoHistory
3695 lines · 77.8 KB · C
RawDownload
#include "lib9.h"#include "draw.h"#include "keyboard.h"#include "tk.h"#include "textw.h" /* * useful text widget info to be found at: * :/coordinate.systems		- what coord. systems are in use * textu.c:/assumed.invariants	- some invariants that must be preserved */ #define istring u.string#define iwin u.win#define imark u.mark#define iline u.line #define FLUSH() flushimage(tk->env->top->display, 1) #define	O(t, e)		((long)(&((t*)0)->e)) /* Layout constants */enum {	Textpadx	= 2,	Textpady	= 0,}; typedef struct Interval {	int	lo;	int	hi;} Interval; typedef struct Mprint Mprint;struct Mprint{	char*	buf;	int	ptr;	int	len;}; typedef struct TkDump TkDump;struct TkDump{	int	sgml;	int	metrics;}; staticTkOption dumpopts[] ={	"sgml",		OPTbool,	O(TkDump, sgml),	nil,	"metrics",	OPTbool,	O(TkDump, metrics),	nil,	nil}; staticTkStab tkcompare[] ={	"<",		TkLt,	"<=",		TkLte,	"==",		TkEq,	">=",		TkGte,	">",		TkGt,	"!=",		TkNeq,	nil}; staticTkOption textopts[] ={	"wrap",			OPTstab, O(TkText, opts[TkTwrap]),	tkwrap,	"spacing1",		OPTnndist, O(TkText, opts[TkTspacing1]),	(void *)O(Tk, env),	"spacing2",		OPTnndist, O(TkText, opts[TkTspacing2]),	(void *)O(Tk, env),	"spacing3",		OPTnndist, O(TkText, opts[TkTspacing3]),	(void *)O(Tk, env),	"tabs",			OPTtabs, O(TkText, tabs), 		(void *)O(Tk, env),	"xscrollcommand",	OPTtext, O(TkText, xscroll),		nil,	"yscrollcommand",	OPTtext, O(TkText, yscroll),		nil,	"insertwidth",		OPTnndist, O(TkText, inswidth),		nil,	"tagshare",		OPTwinp, O(TkText, tagshare),		nil,	"propagate",		OPTstab, O(TkText, propagate),	tkbool,	"selectborderwidth",	OPTnndist, O(TkText, sborderwidth), nil,	nil}; #define CNTL(c) ((c)&0x1f)#define DEL 0x7f static TkEbind tktbinds[] = {	{TkButton1P,		"%W tkTextButton1 %X %Y"},	{TkButton1P|TkMotion,	"%W tkTextSelectTo %X %Y"},	{TkButton1P|TkDouble,	"%W tkTextSelectTo %X %Y double"},	{TkButton1R,		"%W tkTextButton1R"},	{TkButton2P,		"%W scan mark %x %y"},	{TkButton2P|TkMotion,	"%W scan dragto %x %y"},	{TkKey,			"%W tkTextInsert {%A}"},	{TkKey|CNTL('a'),	"%W tkTextSetCursor {insert linestart}"},	{TkKey|Home,		"%W tkTextSetCursor {insert linestart}"},	{TkKey|CNTL('<'),	"%W tkTextSetCursor {insert linestart}"},	{TkKey|CNTL('b'),	"%W tkTextSetCursor insert-1c"},	{TkKey|Left,		"%W tkTextSetCursor insert-1c"},	{TkKey|CNTL('d'),	"%W delete insert"},	{TkKey|CNTL('e'),	"%W tkTextSetCursor {insert lineend}"}, 	{TkKey|End,		"%W tkTextSetCursor {insert lineend}"}, 	{TkKey|CNTL('>'),	"%W tkTextSetCursor {insert lineend}"}, 	{TkKey|CNTL('f'),	"%W tkTextSetCursor insert+1c"},	{TkKey|Right,		"%W tkTextSetCursor insert+1c"},	{TkKey|CNTL('h'),	"%W tkTextDelIns -c"},	{TkKey|DEL,		"%W tkTextDelIns +c"},	{TkKey|CNTL('k'),	"%W delete insert {insert lineend}"},	{TkKey|CNTL('n'),	"%W tkTextSetCursor {insert+1l}"},	{TkKey|Down,		"%W tkTextSetCursor {insert+1l}"},	{TkKey|CNTL('o'),       "%W tkTextInsert {\n}; %W mark set insert insert-1c"},	{TkKey|CNTL('p'),	"%W tkTextSetCursor {insert-1l}"},	{TkKey|Up,		"%W tkTextSetCursor {insert-1l}"},	{TkKey|CNTL('u'),	"%W tkTextDelIns -l"},	{TkKey|CNTL('v'),	"%W yview scroll 0.75 page"},	{TkKey|Pgdown,	"%W yview scroll 0.75 page"},	{TkKey|CNTL('w'),	"%W tkTextDelIns -w"},	{TkKey|Pgup,	"%W yview scroll -0.75 page"},	{TkFocusout,            "%W tkTextCursor delete"},	{TkKey|APP|'\t',	""},	{TkKey|BackTab,		""},}; static int	tktclickmatch(TkText *, int, int, int, TkTindex *);static void	tktdoubleclick(TkText *, TkTindex *, TkTindex *);static char* 	tktdrawline(Image*, Tk*, TkTline*, Point);static void	tktextcursordraw(Tk *, int);static char* 	tktsetscroll(Tk*, int);static void	tktsetclip(Tk *);static char* 	tktview(Tk*, char*, char**, int, int*, int, int);static Interval tkttranslate(Tk*, Interval, int);static void 	tktfixscroll(Tk*, Point);static void 	tktnotdrawn(Tk*, int, int, int);static void	tktdrawbg(Tk*, int, int, int);static int	tktwidbetween(Tk*, int, TkTindex*, TkTindex*);static int	tktpostspace(Tk*, TkTline*);static int	tktprespace(Tk*, TkTline*);static void	tktsee(Tk*, TkTindex*, int);static Point	tktrelpos(Tk*);static void	autoselect(Tk*, void*, int);static void	blinkreset(Tk*); /* debugging */extern int tktdbg;extern void tktprinttext(TkText*);extern void tktprintindex(TkTindex*);extern void tktprintitem(TkTitem*);extern void tktprintline(TkTline*);extern void tktcheck(TkText*, char*);extern int tktutfpos(char *, int); char*tktext(TkTop *t, char* arg, char **ret){	Tk *tk;	char *e;	TkEnv *ev;	TkTline *l;	TkTitem *it = nil;	TkName *names = nil;	TkTtaginfo *ti = nil;	TkOptab tko[3];	TkTmarkinfo *mi = nil;	TkText *tkt, *tktshare; 	tk = tknewobj(t, TKtext, sizeof(Tk)+sizeof(TkText));	if(tk == nil)		return TkNomem; 	tkt = TKobj(TkText, tk); 	tk->relief = TKsunken;	tk->borderwidth = 2;	tk->ipad.x = Textpadx * 2;	tk->ipad.y = Textpady * 2;	tk->flag |= Tktakefocus;	tkt->sborderwidth = 0;	tkt->inswidth = 2;	tkt->cur_flag = 0;	/* text cursor doesn't show up initially */	tkt->opts[TkTwrap] = Tkwrapchar;	tkt->opts[TkTrelief] = TKflat;	tkt->opts[TkTjustify] = Tkleft;	tkt->propagate = BoolX; 	tko[0].ptr = tk;	tko[0].optab = tkgeneric;	tko[1].ptr = tkt;	tko[1].optab = textopts;	tko[2].ptr = nil; 	tk->req.width = tk->env->wzero*Textwidth;	tk->req.height = tk->env->font->height*Textheight; 	names = nil;	e = tkparse(t, arg, tko, &names);	if(e != nil)		goto err;	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));	if(names == nil) {		/* tkerr(t, arg); XXX */		e = TkBadwp;		goto err;	} 	if(tkt->tagshare != nil) {		tkputenv(tk->env);		tk->env = tkt->tagshare->env;		tk->env->ref++;	} 	if(tk->flag&Tkdisabled)		tkt->inswidth = 0; 	if(tkt->tabs == nil) {		tkt->tabs = malloc(sizeof(TkTtabstop));		if(tkt->tabs == nil) 			goto err;		tkt->tabs->pos = 8*tk->env->wzero;		tkt->tabs->justify = Tkleft;		tkt->tabs->next = nil;	} 	if(tkt->tagshare != nil) {		tktshare = TKobj(TkText, tkt->tagshare);		tkt->tags = tktshare->tags;		tkt->nexttag = tktshare->nexttag;	}	else {		/* Note: sel should have id == TkTselid == 0 */		e = tktaddtaginfo(tk, "sel", &ti);		if(e != nil)			goto err; 		tkputenv(ti->env);		ti->env = tknewenv(t);		if(ti->env == nil)			goto err; 		ev = ti->env;		ev->colors[TkCbackgnd] = tk->env->colors[TkCselectbgnd];		ev->colors[TkCbackgndlght] = tk->env->colors[TkCselectbgndlght];		ev->colors[TkCbackgnddark] = tk->env->colors[TkCselectbgnddark];		ev->colors[TkCforegnd] = tk->env->colors[TkCselectfgnd];		ev->set = (1<<TkCbackgnd)|(1<<TkCbackgndlght)|			  (1<<TkCbackgnddark)|(1<<TkCforegnd); 		ti->opts[TkTborderwidth] = tkt->sborderwidth;		if(tkt->sborderwidth > 0)			ti->opts[TkTrelief] = TKraised;	} 	e = tktaddmarkinfo(tkt, "current", &mi);	if(e != nil)		goto err; 	e = tktaddmarkinfo(tkt, "insert", &mi);	if(e != nil)		goto err; 	tkt->start.flags = TkTfirst|TkTlast;	tkt->end.flags = TkTlast; 	e = tktnewitem(TkTnewline, 0, &it); 	if(e != nil)		goto err; 	e = tktnewline(TkTfirst|TkTlast, it, &tkt->start, &tkt->end, &l);	if(e != nil)		goto err; 	e = tktnewitem(TkTmark, 0, &it);	if(e != nil)		goto err; 	it->next = l->items;	l->items = it;	it->imark = mi;	mi->cur = it;	tkt->nlines = 1;	tkt->scrolltop[Tkvertical] = -1;	tkt->scrolltop[Tkhorizontal] = -1;	tkt->scrollbot[Tkvertical] = -1;	tkt->scrollbot[Tkhorizontal] = -1; 	if(tkt->tagshare != nil)		tk->binds = tkt->tagshare->binds;	else {		e = tkbindings(t, tk, tktbinds, nelem(tktbinds)); 		if(e != nil)			goto err;	}	if (tkt->propagate == BoolT) {		if ((tk->flag & Tksetwidth) == 0)			tk->req.width = tktmaxwid(tkt->start.next);		if ((tk->flag & Tksetheight) == 0)			tk->req.height = tkt->end.orig.y;	} 	e = tkaddchild(t, tk, &names);	tkfreename(names);	if(e != nil)		goto err;	tk->name->link = nil; 	return tkvalue(ret, "%s", tk->name->name); err:	/* XXX it's possible there's a memory leak here */	tkfreeobj(tk);	return e;} /* * There are four coordinate systems of interest: *	S - screen coordinate system (i.e. top left corner of *		inferno screen is (0,0) in S space.) *	I - image coordinate system (i.e. top left corner of *		tkimageof(this widget) is (0,0) in I space.) *	T - text coordinate system (i.e., top left of first line *		is at (0,0) in T space.) *	V - view coordinate system (i.e., top left of visible *		portion of widget is at (0,0) in V space.) * *	A point P in the four systems (Ps, Pi, Pt, Pv) satisfies: *		Pt = Ps - deltast *		Pv = Ps - deltasv *		Pv = Pi - deltaiv *	(where deltast is vector from S origin to T origin; *	     deltasv is vector from S origin to V origin; *	     deltaiv is vector from I origin to V origin) * *	We keep deltatv, deltasv, and deltaiv in tkt. *	Deltatv is updated by scrolling. *	Deltasv is updated by geom changes: *		tkposn(tk)+ipad/2 *	Deltaiv is affected by geom changes and the call to the draw function: *		tk->act+orig+ipad/2+(bw,bw) (orig is the parameter to tkdrawtext), * *	We can derive *		Ps = Pt + deltast *		   = Pt +  deltasv - deltatv * *		Pv = Pt - deltatv * * Here are various coordinates in the text widget according * to which coordinate system they use: * *	S - Mouse coordinates (coming in to tktextevent); *		the deltasv parameter to tkdrawtext; *		coords in tkt->image, where drawing is done to *		(to get same bit-alignment as screen, for fast transfer) *	T - orig in TkTlines *	V - %x,%y delivered via binds to TkText or its tags  * Note deltasv changes underneath us, so is calculated on the fly * when it needs to be (in tktextevent). * */static voidtktsetdeltas(Tk *tk, Point orig){	TkText *tkt = TKobj(TkText, tk); 	tkt->deltaiv.x = orig.x + tk->act.x + tk->ipad.x/2 + tk->borderwidth;	tkt->deltaiv.y = orig.y + tk->act.y + tk->ipad.y/2 + tk->borderwidth;} static Pointtktrelpos(Tk *sub){	Tk *tk;	TkTindex ix;	Rectangle r;	Point ans; 	tk = sub->parent;	if(tk == nil)		return ZP; 	if(tktfindsubitem(sub, &ix)) {		r = tktbbox(tk, &ix);		ans.x = r.min.x;		ans.y = r.min.y;		return r.min;	}	return ZP;} static voidtktreplclipr(Image *dst, Rectangle r){	int locked; 	locked = lockdisplay(dst->display);	replclipr(dst, 0, r);	if(locked)		unlockdisplay(dst->display);} char*tkdrawtext(Tk *tk, Point orig){	int vh;	Image *dst;	TkText *tkt;	TkTline *l, *lend;	Point p, deltait;	Rectangle oclipr;	int reldone = 1;	char *e;	tkt = TKobj(TkText, tk);	dst = tkimageof(tk);	if (dst == nil)		return nil;	tkt->image = dst;	tktsetdeltas(tk, orig);	tkt->tflag |= TkTdrawn|TkTdlocked;	oclipr = dst->clipr;	tktsetclip(tk); 	if(tk->flag&Tkrefresh) {		reldone = 0;		tktnotdrawn(tk, 0, tkt->end.orig.y, 1);	}	tk->flag &= ~Tkrefresh; 	deltait = subpt(tkt->deltaiv, tkt->deltatv);	vh = tk->act.height - tk->ipad.y/2;	lend = &tkt->end;	for(l = tkt->start.next; l != lend; l = l->next) {		if(l->orig.y+l->height < tkt->deltatv.y)			continue;		if(l->orig.y > tkt->deltatv.y + vh)			break;		if(!(l->flags&TkTdrawn)) {			e = tktdrawline(dst, tk, l, deltait);			if(e != nil)				return e;		}	} 	tktreplclipr(dst, oclipr);	if(!reldone) {		p.x = orig.x + tk->act.x;		p.y = orig.y + tk->act.y;		tkdrawrelief(dst, tk, p, TkCbackgnd, tk->relief);	}	tkt->tflag &= ~TkTdlocked; 	return nil;} /* * Set the clipping rectangle of the destination image to the * intersection of the current clipping rectangle and the area inside * the text widget that needs to be redrawn. * The caller should save the old one and restore it later. */static voidtktsetclip(Tk *tk){	Rectangle r;	Image *dst;	TkText *tkt = TKobj(TkText, tk); 	dst = tkt->image;	r.min = tkt->deltaiv;	r.max.x = r.min.x + tk->act.width - tk->ipad.x / 2;	r.max.y = r.min.y + tk->act.height - tk->ipad.y / 2; 	if(!rectclip(&r, dst->clipr))		r.max = r.min;	tktreplclipr(dst, r);} static char*tktdrawline(Image *i, Tk *tk, TkTline *l, Point deltait){	Tk *sub;	Font *f;	Image *bg;	Point p, q;	Rectangle r;	TkText *tkt;	TkTitem *it, *z;	int bevtop, bevbot;	TkEnv *e, *et, *env;	int *opts;	int o, bd, ul, ov, h, w, la, lh, cursorx, join;	char *err; 	env = mallocz(sizeof(TkEnv), 0);	if(env == nil)		return TkNomem;	opts = mallocz(TkTnumopts*sizeof(int), 0);	if(opts == nil) {		free(env);

Lines 1–500 of 3695.