426 lines · 7.6 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <draw.h>#include <mouse.h>#include <keyboard.h>#include <thread.h>#include <plumb.h>#include <regexp.h>#include "w.h" extern void mesgmenuhit(int, Mouse); typedef struct Highlight Highlight; struct Highlight{	char *pattern;	ulong color;	Reprog *re;	Image *i;}; Highlight hilights[] = {	/* diff */	{ "^--- a.*$", 0x458588ff, nil },	{ "^\\+\\+\\+ b.*$", 0x458588ff, nil },	{ "^-[^\\-].*$",   0xcc241dFF, nil },	{ "^\\+.*$", 0x98971AFF, nil },	/* quote */	{ "^>.*$", 0x928374FF, nil },}; enum{	Scrollwidth = 12,	Padding = 4,}; enum{	Msnarf,	Mplumb,};char *menu2str[] = {	"snarf",	"plumb",	nil,};Menu menu2 = { menu2str }; Image*findhilight(Text *t, int e){	Image *h;	char c, *s;	int i; 	h = nil;	s = t->data + t->lines[t->nlines - 1];	c = t->data[e];	t->data[e] = 0;	for(i = 0; i < nelem(hilights); i++)		if(regexec(hilights[i].re, s, nil, 0)){			h = hilights[i].i;			break;		}	t->data[e] = c;	return h;} voidcomputelines(Text *t){	int i, x, w, l, c, s, wrap;	Rune r;	Image *h; 	t->lines[0] = 0;	t->nlines = 1;	w = Dx(t->textr);	x = 0;	wrap = 0;	h = nil;	for(i = 0; i < t->ndata; ){		s = i;		c = chartorune(&r, &t->data[i]);		if(r == '\n'){			if(i + c == t->ndata){				t->high[t->nlines - 1] = findhilight(t, i+c);				break;			}			if(wrap)				t->high[t->nlines - 1] = h;			else				t->high[t->nlines - 1] = findhilight(t, i+c);			t->lines[t->nlines++] = i + c;			x = 0;			wrap = 0;			h = nil;		}else{			l = 0;			if(r == '\t'){				x += stringwidth(t->font, "    ");			}else{				l = runestringnwidth(t->font, &r, 1);				x += l;				if(r == ' ')					s = i + 1;			}			if(x > w){				i = s;				h = findhilight(t, i);				t->high[t->nlines - 1] = h;				t->lines[t->nlines++] = i;				x = l;				wrap = 1;			}		}		i += c;	}} intindexat(Text *t, Point p){	int line, i, s, e, x, c, l;	Rune r; 	if(!ptinrect(p, t->textr))		return -1;	line = t->offset + ((p.y - t->textr.min.y) / font->height);	s = t->lines[line];	if(line+1 >= t->nlines)		e = t->ndata;	else		e = t->lines[line+1] - 2; /* make sure we exclude the newline */	c = 0;	x = t->textr.min.x;	for(i = s; i < e; ){		c = chartorune(&r, &t->data[i]);		if(r == '\t')			l = stringwidth(t->font, "    ");		else			l = runestringnwidth(t->font, &r, 1);		if(x <= p.x && p.x <= x+l)			break;		i += c;		x += l;	}	if(r == '\n')		i -= c;	return i;} voidtextinit(Text *t, Image *b, Rectangle r, Font *f, Image *cols[NCOLS]){	int i; 	memset(t, 0, sizeof *t);	t->b = b;	t->font = f;	t->s0 = -1;	t->s1 = -1;	t->offset = 0;	textresize(t, r);	memmove(t->cols, cols, sizeof t->cols);	for(i = 0; i < nelem(hilights); i++){		hilights[i].re = regcomp(hilights[i].pattern);		hilights[i].i  = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, hilights[i].color);	}} voidtextset(Text *t, char *data, usize ndata){	t->s0 = -1;	t->s1 = -1;	t->offset = 0;	t->data = data;	t->ndata = ndata;	memset(t->high, 0, Maxlines*sizeof(Image*));	computelines(t);} voidtextresize(Text *t, Rectangle r){	t->r = r;	t->vlines = Dy(t->r) / t->font->height;	t->scrollr = rectaddpt(Rect(0, 0, Scrollwidth, Dy(r)), r.min);	t->textr = r;	t->textr.min.x = t->scrollr.max.x + Padding;	if(t->nlines > 0)		computelines(t);} intselected(Text *t, int index){	int s0, s1; 	if(t->s0 < 0 || t->s1 < 0)		return 0;	s0 = t->s0 < t->s1 ? t->s0 : t->s1;	s1 = t->s0 > t->s1 ? t->s0 : t->s1;	return s0 <= index && index <= s1;} voiddrawline(Text *t, int index){	int i, s, e, sel;	Point p;	Rune r;	Image *fg, *bg; 	s = t->lines[t->offset+index];	if(t->offset+index+1 >= t->nlines)		e = t->ndata;	else		e = t->lines[t->offset+index+1];	p = addpt(t->textr.min, Pt(0, index*font->height));	for(i = s; i < e; ){		sel = selected(t, i);		fg = sel ? t->cols[HTEXT] : t->cols[TEXT];		bg = sel ? t->cols[HIGH]  : t->cols[BACK];		if(!sel && t->high[t->offset+index] != nil)			fg = t->high[t->offset+index];		i += chartorune(&r, &t->data[i]);		if(r == '\n')			if(s + 1 == e) /* empty line */				r = L' ';			else				continue;		if(r == '\t')			p = stringbg(t->b, p, fg, ZP, t->font, "    ", bg, ZP);		else			p = runestringnbg(t->b, p, fg, ZP, t->font, &r, 1, bg, ZP);	}} voidtextdraw(Text *t){	int i, h, y;	Rectangle sr; 	draw(t->b, t->r, t->cols[BACK], nil, ZP);	draw(t->b, t->scrollr, t->cols[BORD], nil, ZP);	border(t->b, t->scrollr, 0, t->cols[TEXT], ZP);	if(t->nlines > 0){		h = ((double)t->vlines / t->nlines) * Dy(t->scrollr);		y = ((double)t->offset / t->nlines) * Dy(t->scrollr);		sr = Rect(t->scrollr.min.x + 1, t->scrollr.min.y + y + 1, t->scrollr.max.x - 1, t->scrollr.min.y + y + h - 1);	}else		sr = insetrect(t->scrollr, -1);	draw(t->b, sr, t->cols[BACK], nil, ZP);	for(i = 0; i < t->vlines; i++){		if(t->offset+i >= t->nlines)			break;		drawline(t, i);	}	flushimage(display, 1);} staticvoidscroll(Text *t, int lines){	if(t->nlines <= t->vlines)		return;	if(lines < 0 && t->offset == 0)		return;	if(lines > 0 && t->offset + t->vlines >= t->nlines)		return;	t->offset += lines;	if(t->offset < 0)		t->offset = 0;	if(t->offset + t->nlines%t->vlines >= t->nlines)		t->offset = t->nlines - t->nlines%t->vlines;	textdraw(t);} voidtextkeyboard(Text *t, Rune k){	switch(k){	case Kup:		scroll(t, -1);		break;	case Kdown:		scroll(t, 1);		break;	case Kpgup:		scroll(t, -t->vlines);		break;	case Kpgdown:		scroll(t, t->vlines);		break;	case Khome:		scroll(t, -t->nlines);		break;	case Kend:		scroll(t, t->nlines);		break;	}} voidsnarfsel(Text *t){	int fd, s0, s1; 	if(t->s0 < 0 || t->s1 < 0)		return;	fd = open("/dev/snarf", OWRITE);	if(fd < 0)		return;	s0 = t->s0 < t->s1 ? t->s0 : t->s1;	s1 = t->s0 > t->s1 ? t->s0 : t->s1;	write(fd, &t->data[s0], s1 - s0 + 1);	close(fd);} voidplumbsel(Text *t){	int fd, s0, s1;	char *s; 	if(t->s0 < 0 || t->s1 < 0)		return;	fd = plumbopen("send", OWRITE);	if(fd < 0)		return;	s0 = t->s0 < t->s1 ? t->s0 : t->s1;	s1 = t->s0 > t->s1 ? t->s0 : t->s1;	s = smprint("%.*s", s1 - s0 + 1, &t->data[s0]);	plumbsendtext(fd, argv0, nil, nil, s);	free(s);	close(fd);} voidmenu2hit(Text *t, Mousectl *mc){	int n; 	n = menuhit(2, mc, &menu2, nil);	switch(n){		case Msnarf:			snarfsel(t);			break;		case Mplumb:			plumbsel(t);			break;	}} voidtextmouse(Text *t, Mousectl *mc){	static selecting = 0;	Point p;	int n; 	if(ptinrect(mc->xy, t->scrollr)){		if(mc->buttons == 1){			n = (mc->xy.y - t->scrollr.min.y) / font->height;			scroll(t, -n);		}else if(mc->buttons == 2){			t->offset = (mc->xy.y - t->scrollr.min.y) * t->nlines / Dy(t->scrollr);			textdraw(t);		}else if(mc->buttons == 4){			n = (mc->xy.y - t->scrollr.min.y) / font->height;			scroll(t, n);		}	}else if(ptinrect(mc->xy, t->textr)){		if(mc->buttons == 0)			selecting = 0;		if(mc->buttons == 1){			if(!selecting){				selecting = 1;				t->s0 = t->s1 = -1;				n = indexat(t, mc->xy);				if(n < 0)					return;				t->s0 = n;				t->s1 = -1;				textdraw(t);			}else{				n = indexat(t, mc->xy);				if(n < 0)					return;				t->s1 = n;			}			for(readmouse(mc); mc->buttons == 1; readmouse(mc)){				p = mc->xy;				if(p.y <= t->textr.min.y){					scroll(t, -1);					p.y = t->textr.min.y + 1;				}else if(p.y >= t->textr.max.y){					scroll(t, 1);					p.y = t->textr.max.y - 1;				}				n = indexat(t, p);				if(n < 0)					break;				t->s1 = n;				textdraw(t);			}		}else if(mc->buttons == 2){			menu2hit(t, mc);		}else if(mc->buttons == 4){			mesgmenuhit(3, *mc);		}else if(mc->buttons == 8){			n = mousescrollsize(t->vlines);			scroll(t, -n);		}else if(mc->buttons == 16){			n = mousescrollsize(t->vlines);			scroll(t, n);		}	}}