889 lines · 17.2 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <draw.h>#include <memdraw.h>#include <thread.h>#include <cursor.h>#include <mouse.h>#include <keyboard.h>#include <frame.h>#include <plumb.h>#include <html.h>#include "dat.h"#include "fns.h" Image	*tagcols[NCOL];Image	*textcols[NCOL];  voidtextinit(Text *t, Image *b, Rectangle r, Font *f, Image *cols[NCOL]){	t->all = r;	t->scrollr = r;	t->scrollr.max.x = r.min.x+Scrollsize;	t->lastsr = ZR;	r.min.x += Scrollsize+Scrollgap;	t->rs.nr = 0;	memmove(t->Frame.cols, cols, sizeof t->Frame.cols);	textredraw(t, r, f, b);} voidtextredraw(Text *t, Rectangle r, Font *f, Image *b){	Rectangle r1; 	frinit(t, r, f, b, t->Frame.cols);	r1 = t->r;	r1.min.x -= Scrollsize+Scrollgap;	/* back fill to scroll bar */	draw(t->b, r1, t->cols[BACK], nil, ZP);	t->maxtab = Maxtab*stringwidth(f, "0");	textfill(t);	textsetselect(t, t->q0, t->q1);} inttextresize(Text *t, Image *b, Rectangle r){	if(Dy(r) > 0)		r.max.y -= Dy(r)%t->font->height;	else		r.max.y = r.min.y; 	t->all = r;	t->scrollr = r;	t->scrollr.max.x = r.min.x+Scrollsize;	t->lastsr = ZR;	r.min.x += Scrollsize+Scrollgap;	frclear(t, 0);	textredraw(t, r, t->font, b);	if(t->what == Textarea)		textscrdraw(t);	return r.max.y;} voidtextclose(Text *t){	closerunestr(&t->rs);	frclear(t, 1);} voidtextinsert(Text *t, uint q0, Rune *r, uint n){	if(n == 0)		return; 	t->rs.r = runerealloc(t->rs.r, t->rs.nr+n);	runemove(t->rs.r+q0+n, t->rs.r+q0, t->rs.nr-q0);	runemove(t->rs.r+q0, r, n);	t->rs.nr += n;	if(q0 < t->q1)		t->q1 += n;	if(q0 < t->q0)		t->q0 += n;	if(q0 < t->org)		t->org += n;	else if(q0 <= t->org+t->nchars)		frinsert(t, r, r+n, q0-t->org);} voidtextfill(Text *t){	Rune *rp;	int i, n, m, nl; 	if(t->lastlinefull)		return;	rp = runemalloc(BUFSIZE*8);	do{		n = t->rs.nr-(t->org+t->nchars);		if(n == 0)			break;		if(n > 2000)	/* educated guess at reasonable amount */			n = 2000;		runemove(rp, t->rs.r+(t->org+t->nchars), n);		/*		 * it's expensive to frinsert more than we need, so		 * count newlines.		 */		nl = t->maxlines-t->nlines;		m = 0;		for(i=0; i<n; ){			if(rp[i++] == '\n'){				m++;				if(m >= nl)					break;			}		}		frinsert(t, rp, rp+i, t->nchars);	}while(t->lastlinefull == FALSE);	free(rp);} voidtextdelete(Text *t, uint q0, uint q1){	uint n, p0, p1; 	n = q1-q0;	if(n == 0)		return; 	runemove(t->rs.r+q0, t->rs.r+q1, t->rs.nr-q1);	t->rs.nr -= n;	if(q0 < t->q0)		t->q0 -= min(n, t->q0-q0);	if(q0 < t->q1)		t->q1 -= min(n, t->q1-q0);	if(q1 <= t->org)		t->org -= n;	else if(q0 < t->org+t->nchars){		p1 = q1 - t->org;		if(p1 > t->nchars)			p1 = t->nchars;		if(q0 < t->org){			t->org = q0;			p0 = 0;		}else			p0 = q0 - t->org;		frdelete(t, p0, p1);		textfill(t);	}	t->rs.r[t->rs.nr] = L'\0';} inttextbswidth(Text *t, Rune c){	uint q, eq;	Rune r;	int skipping; 	/* there is known to be at least one character to erase */	if(c == 0x08)	/* ^H: erase character */		return 1;	q = t->q0;	skipping = TRUE;	while(q > 0){		r = t->rs.r[q-1];		if(r == '\n'){		/* eat at most one more character */			if(q == t->q0)	/* eat the newline */				--q;			break;		}		if(c == 0x17){			eq = isalnum(r);			if(eq && skipping)	/* found one; stop skipping */				skipping = FALSE;			else if(!eq && !skipping)				break;		}		--q;	}	return t->q0-q;} voidtexttype(Text *t, Rune r){	uint q0, q1;	int nb, n;	int nr;	Rune *rp; 	if(t->what!=Textarea && r=='\n'){		if(t->what==Urltag)			get(t, t, XXX, XXX, nil, 0);		return;	}	if(t->what==Tag && (r==Kscrollonedown || r==Kscrolloneup))		return; 	nr = 1;	rp = &r;	switch(r){	case Kleft:		if(t->q0 > 0)			textshow(t, t->q0-1, t->q0-1, TRUE);		return;	case Kright:		if(t->q1 < t->rs.nr)			textshow(t, t->q1+1, t->q1+1, TRUE);		return;	case Kdown:		n = t->maxlines/3;		goto case_Down;	case Kscrollonedown:		n = mousescrollsize(t->maxlines);		if(n <= 0)			n = 1;		goto case_Down;	case Kpgdown:		n = 2*t->maxlines/3;	case_Down:		q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height));		textsetorigin(t, q0, TRUE);		return;	case Kup:		n = t->maxlines/3;		goto case_Up;	case Kscrolloneup:		n = mousescrollsize(t->maxlines);		goto case_Up;	case Kpgup:		n = 2*t->maxlines/3;	case_Up:		q0 = textbacknl(t, t->org, n);		textsetorigin(t, q0, TRUE);		return;	case Khome:		textshow(t, 0, 0, FALSE);		return;	case Kend:		textshow(t, t->rs.nr, t->rs.nr, FALSE);		return;	case 0x01:	/* ^A: beginning of line */		/* go to where ^U would erase, if not already at BOL */		nb = 0;		if(t->q0>0 && t->rs.r[t->q0-1]!='\n')			nb = textbswidth(t, 0x15);		textshow(t, t->q0-nb, t->q0-nb, TRUE);		return;	case 0x05:	/* ^E: end of line */		q0 = t->q0;		while(q0<t->rs.nr && t->rs.r[q0]!='\n')			q0++;		textshow(t, q0, q0, TRUE);		return;	}	if(t->q1 > t->q0)		cut(t, t, TRUE, TRUE, nil, 0); 	textshow(t, t->q0, t->q0, TRUE);	switch(r){	case 0x08:	/* ^H: erase character */	case 0x15:	/* ^U: erase line */	case 0x17:	/* ^W: erase word */		if(t->q0 == 0)	/* nothing to erase */			return;		nb = textbswidth(t, r);		q1 = t->q0;		q0 = q1-nb;		/* if selection is at beginning of window, avoid deleting invisible text */		if(q0 < t->org){			q0 = t->org;			nb = q1-q0;		}		if(nb > 0){			textdelete(t, q0, q0+nb);			textsetselect(t, q0, q0);		}		return;	}	/* otherwise ordinary character; just insert */	textinsert(t, t->q0, &r, 1);	if(rp != &r)		free(rp);	textsetselect(t, t->q0+nr, t->q0+nr);	if(t->what == Textarea)		textscrdraw(t);} static	Text	*clicktext;static	uint	clickmsec;static	Text	*selecttext;static	uint	selectq; /* * called from frame library */voidframescroll(Frame *f, int dl){	if(f != &selecttext->Frame)		error("frameselect not right frame");	textframescroll(selecttext, dl);} voidtextframescroll(Text *t, int dl){	uint q0; 	if(dl == 0){		scrsleep(100);		return;	}	if(dl < 0){		q0 = textbacknl(t, t->org, -dl);		if(selectq > t->org+t->p0)			textsetselect(t, t->org+t->p0, selectq);		else			textsetselect(t, selectq, t->org+t->p0);	}else{		if(t->org+t->nchars == t->rs.nr)			return;		q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+dl*t->font->height));		if(selectq > t->org+t->p1)			textsetselect(t, t->org+t->p1, selectq);		else			textsetselect(t, selectq, t->org+t->p1);	}	textsetorigin(t, q0, TRUE);} voidtextselect(Text *t){	uint q0, q1;	int b, x, y;	int state; 	selecttext = t;	/*	 * To have double-clicking and chording, we double-click	 * immediately if it might make sense.	 */	b = mouse->buttons;	q0 = t->q0;	q1 = t->q1;	selectq = t->org+frcharofpt(t, mouse->xy);	if(clicktext==t && mouse->msec-clickmsec<500)	if(q0==q1 && selectq==q0){		textdoubleclick(t, &q0, &q1);		textsetselect(t, q0, q1);		flushimage(display, 1);		x = mouse->xy.x;		y = mouse->xy.y;		/* stay here until something interesting happens */		do			readmouse(mousectl);		while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3);		mouse->xy.x = x;	/* in case we're calling frselect */		mouse->xy.y = y;		q0 = t->q0;	/* may have changed */		q1 = t->q1;		selectq = q0;	}	if(mouse->buttons == b){		t->Frame.scroll = framescroll;		frselect(t, mousectl);		/* horrible botch: while asleep, may have lost selection altogether */		if(selectq > t->rs.nr)			selectq = t->org + t->p0;		t->Frame.scroll = nil;		if(selectq < t->org)			q0 = selectq;		else			q0 = t->org + t->p0;		if(selectq > t->org+t->nchars)			q1 = selectq;		else			q1 = t->org+t->p1;	}	if(q0 == q1){		if(q0==t->q0 && clicktext==t && mouse->msec-clickmsec<500){			textdoubleclick(t, &q0, &q1);			clicktext = nil;		}else{			clicktext = t;			clickmsec = mouse->msec;		}	}else		clicktext = nil;	textsetselect(t, q0, q1);	flushimage(display, 1);	state = 0;	/* undo when possible; +1 for cut, -1 for paste */	while(mouse->buttons){		mouse->msec = 0;		b = mouse->buttons;		if((b&1) && (b&6)){			if(b & 2){				if(state==-1 && t->what==Textarea){					textsetselect(t, q0, t->q0);					state = 0;				}else if(state != 1){					cut(t, t, TRUE, TRUE, nil, 0);					state = 1;				}			}else{				if(state==1 && t->what==Textarea){					textsetselect(t, q0, t->q1);					state = 0;				}else if(state != -1){					paste(t, t, TRUE, FALSE, nil, 0);					state = -1;				}			}			textscrdraw(t);		}		flushimage(display, 1);		while(mouse->buttons == b)			readmouse(mousectl);		clicktext = nil;	}} voidtextshow(Text *t, uint q0, uint q1, int doselect){	int qe;	int nl;	uint q; 	if(t->what != Textarea){		if(doselect)			textsetselect(t, q0, q1);		return;	}	if(doselect)		textsetselect(t, q0, q1);	qe = t->org+t->nchars;	if(t->org<=q0 && (q0<qe || (q0==qe && qe==t->rs.nr)))		textscrdraw(t);	else{		nl = t->maxlines/4;		q = textbacknl(t, q0, nl);		/* avoid going backwards if trying to go forwards - long lines! */		if(!(q0>t->org && q<t->org))			textsetorigin(t, q, TRUE);		while(q0 > t->org+t->nchars)			textsetorigin(t, t->org+1, FALSE);	}} staticintregion(int a, int b){	if(a < b)		return -1;	if(a == b)		return 0;	return 1;} voidselrestore(Frame *f, Point pt0, uint p0, uint p1){	if(p1<=f->p0 || p0>=f->p1){		/* no overlap */		frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]);		return;	}	if(p0>=f->p0 && p1<=f->p1){		/* entirely inside */		frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);		return;	} 	/* they now are known to overlap */ 	/* before selection */	if(p0 < f->p0){		frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]);		p0 = f->p0;		pt0 = frptofchar(f, p0);	}

Lines 1–500 of 889.