Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/sys/src/cmd/omero/text.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <draw.h>
#include <mouse.h>
#include <ctype.h>
#include <keyboard.h>
#include <frame.h>
#include <9p.h>
#include <bio.h>
#include <b.h>
#include "gui.h"
#include "cook.h"

/* BUG: WIP
 */

static void xinit(Panel*);
static void xterm(Panel*);
static int  xctl(Panel* p, char* ctl);
static long xattrs(Panel* p, char* buf, long sz);
static long xread(Panel* p, void* buf, long cnt, vlong off);
static long xwrite(Panel* p, void* buf, long cnt, vlong off);
static void xdraw(Panel* p, int resize);
static void xmouse(Panel* p, Cmouse* m, Channel* mc);
static void xkeyboard(Panel*, Rune);
static void xtruncate(Panel* p);

Pops textops = {
	.pref = "text:",
	.init = xinit,
	.term = xterm,
	.ctl = xctl,
	.attrs= xattrs,
	.read = xread,
	.write= xwrite,
	.draw = xdraw,
	.mouse = xmouse,
	.keyboard = xkeyboard,
	.truncate = xtruncate,
};

Pops tagops = {
	.pref = "tag:",
	.init = xinit,
	.term = xterm,
	.ctl = xctl,
	.attrs= xattrs,
	.read = xread,
	.write= xwrite,
	.draw = xdraw,
	.mouse = xmouse,
	.keyboard = xkeyboard,
	.truncate = xtruncate,
};

Pops labelops = {
	.pref = "label:",
	.init = xinit,
	.term = xterm,
	.ctl = xctl,
	.attrs= xattrs,
	.read = xread,
	.write= xwrite,
	.draw = xdraw,
	.mouse = xmouse,
	.keyboard = xkeyboard,
	.truncate = xtruncate,
};

Pops buttonops = {
	.pref = "button:",
	.init = xinit,
	.term = xterm,
	.ctl = xctl,
	.attrs= xattrs,
	.read = xread,
	.write= xwrite,
	.draw = xdraw,
	.mouse = xmouse,
	.keyboard = xkeyboard,
	.truncate = xtruncate,
};

static void 
xinit(Panel* p)
{
	char*	s;
	char*	q;
	Rectangle r;

	if (!strncmp(p->name, "text:", 5)){
		p->flags |= Pedit;
		p->wants = Pt(1,1);
	} else if (!strncmp(p->name, "tag:", 4)){
		p->flags |= Pline|Pedit;
		p->wants = Pt(1,0);
	} else
		p->flags |= Pline;
	if (p->flags&Pline)
		p->font = fonts[FB];
	else
		p->font = fonts[FR];

	if (p->flags&Pline){
		s = strchr(p->name, ':');
		q = strchr(s, '.');
		if (q)
			*q = 0;
		p->blks = str2block(s ? s+1 : p->name);
		if (q)
			*q = '.';
	} else
		p->blks = str2block("");
	assert(p->blks);
	settextsize(p);
	r = Rpt(ZP, p->minsz);
	p->f.nchars = 0;

}

static void 
xterm(Panel* p)
{
	if (p->blks == nil)
		sysfatal("xterm on terminated panel");
	blockfree(p->blks);
	p->blks = nil;
	cleanedits(p);
	if (p->loaded)
		frclear(&p->f, 1);
	p->loaded = 0;
}

static int
dirty(Panel* p, int set, int sendev)
{
	int	old;

	if (p->flags&Pline)	// lines do not get dirty
		return 0;
	old = p->flags;
	if (set)
		p->flags |= Pdirty;
	else
		p->flags &= ~Pdirty;
	if (old != p->flags){
		borders(p->file->parent);
		if (sendev)
			event(p, set ? "dirty" : "clean" );
		return 1;
	}
	return 0;
}

static int
gotopos(Panel* p, int npos)
{
	Tblock*	b;
	int	n;

	if (npos == p->froff)
		return 0;
	b = p->blks;
	packblock(b);
	if (npos > b->nr)
		npos = b->nr;
	if (npos > 1){
		n = npos - 1;		// skip this \n
		if (findrln(b, &n)){	// and search for previous one
			if (n > 0)
				n++;
			npos = n;
		}
	}
	if (npos < 0)
		npos = 0;
	if (p->froff != npos){
		p->froff = npos;
		return 1;
	} else
		return 0;
}

static void
mustshow(Panel* p, int npos)
{
	int	h;

	if (npos < p->froff  || npos > p->froff + p->f.nchars){
		if (gotopos(p, npos) && p->loaded){
			h = gettextht(p)/2;
			scrollframe(p, -h);
			xdraw(p, 0);
		}
	}
}

static void
xdraw(Panel* p, int )
{

	if (!p->file || (p->flags&Pdead))
		return;

	/* Problem. For buttons, we create the data from the
	 * file name. But, xinit does not have dfile to set its
	 * length, and we must do that later, i.e. here.
	 */
	if (p->dfile && p->dfile->length == 0LL)
		p->dfile->length = blocksize(p->blks);

	if (hidden(p->file) || Dx(p->rect) < 10 || Dy(p->rect) < p->font->height){
		if (p->loaded)
			frclear(&p->f, 0);
		p->loaded = 0;
		return;
	}
	hidesel(p);
	if (p->loaded)
		frclear(&p->f, 0);
	frinit(&p->f, p->rect, p->font, screen, cols);
	p->loaded = 1;
	filltext(p);
	drawsel(p);
	settextsize(p);
	if (p->flags&Ptag)
		drawtag(p, 0);
}

static void
drawscrollbar(Panel* p, Image* i)
{
	static Image* setcol;
	Rectangle r;
	Rectangle bar;
	int	y0, dy;
	int	ysz;
	int	l;

	if (setcol == nil)
		setcol = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0x5555AAFF);
	bar.max.x = r.max.x = i->r.max.x - Inset;
	bar.min.x = r.min.x = r.max.x - 15;
	r.min.y = i->r.min.y + Inset;
	ysz = 90;
	r.max.y = r.min.y + ysz;
	if (r.max.y > i->r.max.y){
		r.max.y = i->r.max.y;
		ysz = Dy(r);
	}
	draw(i, r, cols[BACK], nil, ZP);
	l = blocklen(p->blks);
	if (l > 0){
		y0 = ysz * p->froff / l;
		dy = ysz * p->f.nchars / l;
		if (dy < 3)
			dy = 3;
	} else {
		y0 = 0;
		dy = Dy(r);
	}
	bar.min.y = r.min.y + y0;
	bar.max.y = bar.min.y + dy;
	draw(i, bar, setcol, nil, ZP);
	border(i, r, 1, cols[BORD], ZP);
}

/* To avoid blinking, we use double buffering during
 * scroll operations. This is because we redraw the frame,
 * and do not shift rectangles around.
 */
static Image*
scrdraw(Panel* p, Image* i)
{
	Rectangle r;

	assert(p->loaded);
	r = Rpt(ZP, Pt(Dx(p->rect), Dy(p->rect)));
	if (i == nil){
		i = allocimage(display, r, screen->chan, 0, CBack);
	}
	draw(i, r, cols[BACK], nil, ZP);
	frclear(&p->f, 0);
	frinit(&p->f, r, p->font, i, cols);
	filltext(p);
	drawsel(p);
	drawscrollbar(p, i);
	draw(screen, p->rect, i, nil, ZP);
	return i;
}

static void
scrdone(Panel* p, Image* i)
{
	draw(i, Rpt(ZP, Pt(Dx(i->r), Dy(i->r))), cols[BACK], nil, ZP);
	frclear(&p->f, 0);
	p->loaded = 0;
	freeimage(i);
	xdraw(p, 0);
}

void
setsel(Panel* p, int ss, int se)
{
	if (ss < se){
		p->ss = ss;
		p->se = se;
	} else {
		p->ss = se;
		p->se = ss;
	}
}


double
jumpscale(Panel* p, Point xy)
{
	double	dy;
	double	dc;

	dy = xy.y - p->rect.min.y;
	if (dy > p->rect.max.y - xy.y)
		dy = p->rect.max.y - xy.y;
	dc = blocklen(p->blks) - p->froff;
	if (dc < p->froff)
		dc = p->froff;
	if (dy < 1)
		dy = 1;
	return dc / dy;
}

static int
undocmd(Panel* p)
{
	int	pos;

	if (undo(p, &pos)){
		dirty(p, hasedits(p), 1);
		mustshow(p, pos);
		return 1;
	} else
		return 0;
}

static int
redocmd(Panel* p)
{
	int	pos;

	if (redo(p, &pos)){
		dirty(p, hasedits(p), 1);
		mustshow(p, pos);
		return 1;
	} else
		return 0;
}


static int
nlines(Rune* r, int n)
{
	int	i, nl;
	int	lc;

	lc = nl = 0;
	for (i = 0; i < n; i++)
		if (r[i] == '\n' || ++lc > 80){
			nl++;
			lc = 0;
		}
			
	return nl;
}

int
textins(Panel* p, Rune* r, int nr, int pos)
{
	int	rc;

	rc = frameins(p, r, nr, pos);
	p->nlines += nlines(r, nr);
	event(p, "ins %11d %11d %.*S", pos, nr, nr, r);
	return rc;
}

void
textdel(Panel* p, Rune* r, int nr, int pos)
{
	if (nr > 0){
		nr = framedel(p, r, nr, pos);
		if (nr){
			p->nlines -= nlines(r, nr);
			event(p, "del %11d %11d", pos, nr);
		}
	}
}

static void
writepsel(Panel* p)
{
	char*	s;

	if ( !(p->flags&Pline)) {
		s = smprint("/devs%s", p->path);
		writefstr("/dev/sel", s);
		free(s);
	}
}

static int
cut(Panel* p, int putsnarf)
{
	char*	s;

	if (p->ss >= p->se)
		return 0;
	free(p->snarf);
	p->nsnarf = p->se - p->ss;
	p->snarf = emalloc9p((p->nsnarf+1) * sizeof(Rune));
	hidesel(p);
	textdel(p, p->snarf, p->nsnarf, p->ss);
	p->se = p->s0 = p->ss;
	drawsel(p);
	filltext(p);
	addedit(p, Del, p->snarf, p->nsnarf, p->ss);
	if (putsnarf){
		p->snarf[p->nsnarf] = 0;
		s = smprint("%S", p->snarf);
		writefstr("/dev/snarf", s);
		free(s);
	}
	return 1;
}

static void
paste(Panel* p, int pos)
{
	Rune*	r;
	char*	s;

	if (s = readfstr("/dev/snarf")){
		r = utf2runes(s, nil, nil);
		free(s);
		free(p->snarf);
		p->snarf = r;
		p->nsnarf = runeslen(r);
		if (p->nsnarf){
			textins(p, p->snarf, p->nsnarf, pos);
			p->s0 = p->ss = pos;
			p->se = pos + p->nsnarf;
			drawsel(p);
			addedit(p, Ins, p->snarf, p->nsnarf, pos);
		}
	}
}

static void
run(Panel* p, int pos, int onsel)
{
	Rune*	r;
	int	s, e;
	int	n;
	char*	c;
	char*	ev;

	r = gettextword(p, pos, &s, &e);
	c = smprint("%S", r);
	if (!wcommand(c)){
		if (onsel)
			ev = "args";
		else
			ev = "exec";
		n = e - s;
		event(p, "%s %11d %S", ev, runenlen(r, n), r);
	}
	free(c);
	free(r);
}

static void
look(Panel* p, int pos)
{
	Rune*	r;
	int	s, e;
	int	n;

	if (r = gettextword(p, pos, &s, &e)){
		assert(e >= s);
		p->ss = s;
		p->se = e;
		n = p->se - p->ss;
		if (n > 0)
			event(p, "look %11d %S", runenlen(r, n), r);
		free(r);
	}
}


static int
findnln(void* a, int , Rune r)
{
	int	c = (int)r;
	int	*np = a;

	if (c == '\n' && --(*np) < 0)
		return 1;
	return 0;
}


/* :xx searches for line number. Else, literal match.
 */
static void
search(Panel* p, char* key)
{
	Tblock*	b;
	int	pos;
	int	epos;
	int	ln;
	Rune*	r;
	Rune*	rp;

	b = p->blks;
	packblock(b);
	if (key[0] == ':' && isdigit(key[1])){
		ln = strtod(key+1, nil) - 1;
		if (ln < 0)
			ln = 0;
		pos = 0;
		while(pos < b->nr && ln > 0 && findln(b, &pos)){
			pos++;
			ln--;
		}
		epos = pos + 1;
		if (epos < b->nr && findln(b, &epos))
			epos++;
	} else {
		r = utf2runes(key, nil, nil);
		if (p->ss != p->se)
			pos = p->ss + 1;
		if (pos < b->nr)
			rp = runestrstr(b->r + pos, r);
		else
			rp = nil;
		if (rp == nil){
			pos = 0;
			rp = runestrstr(b->r + pos, r);
		}
		if (rp == nil)
			epos = pos = 0;
		else {
			pos = rp - b->r;
			epos = pos + runestrlen(r);
		}
		free(r);
	}
	if (pos || epos){
		p->ss = pos;
		p->se = epos;
		mustshow(p, p->ss);
		drawsel(p);
	}
}

static void
xmouse12(Panel* p, Cmouse* m, Channel* mc)
{
	int	updated;

	updated = 0;
	for(;;){
		switch(m->buttons){
		case 0:
			return;
		case 3:
			if (!updated++)
				writepsel(p);
			cut(p, 1);
			if (p->flags&Pedit)
				dirty(p, 1, 1);
			else {
				undo(p, nil);
				p->ss = p->se = 0;
				drawsel(p);
			}
			flushimage(display, 1);
			do {
				recv(mc, m);
			} while (m->buttons == 3);
			break;
		case 5:
			if (p->flags&Pedit)
			if (undo(p, nil))
				dirty(p, hasedits(p), 0);
			flushimage(display, 1);
			do {
				recv(mc, m);
			} while (m->buttons == 5);
			break;
		default:
			recv(mc, m);
			break;
		}
	}
}

static void
xmouse13(Panel* p, Cmouse* m, Channel* mc)
{
	int	updated;

	updated = 0;
	for(;;){
		switch(m->buttons){
		case 0:
			return;
		case 5:
			if (p->flags&Pedit){
				cut(p, 0);
				paste(p, p->ss);
				settextsize(p);
				dirty(p, 1, 1);
			}
			if (!updated++)
				writepsel(p);
			flushimage(display, 1);
			do {
				recv(mc, m);
			} while (m->buttons == 5);
			break;
		case 3:
			if (p->flags&Pedit)
			if (undo(p, nil))
				dirty(p, hasedits(p), 0);
			flushimage(display, 1);
			do {
				recv(mc, m);
			} while (m->buttons == 3);
			break;
		default:
			recv(mc, m);
			break;
		}
	}
}

static void
xmouse1(Panel* p, Cmouse* m, Channel* mc, Point xy, int pos)
{
	Rune*	r;
	int	ss, se;
	int	updated;
	int	old;

	if (m->flag == CMdouble){
		r = gettextword(p, pos, &ss, &se);
		if (r){
			free(r);
			p->ss = ss;
			p->se = se;
			writepsel(p);
		}
	} else
		p->ss = p->se = pos;
	p->s0 = p->ss;
	drawsel(p);
	flushimage(display, 1);
	updated = 0;
	old = -1;
	for(;;){
		recv(mc, m);
		xy = m->xy;
		panelok(p);
		switch(m->buttons){
		case 0:
			return;
		case 1:	// slide
			pos = p->froff + frcharofpt(&p->f, xy);
			if (pos == old){
				sleep(10);
			} else {
				old = pos;
				setsel(p, p->s0, pos);
				drawsel(p);
				if (p->ss != p->se && !updated++)
					writepsel(p);
				flushimage(display, 1);
			}
			break;
		case 3:	// 1-2 chord
			xmouse12(p, m, mc);
			return;
		case 5: // 1-3 chort
			xmouse13(p, m, mc);
			return;
		default:
			do {
				recv(mc, m);
			} while (m->buttons);
		}
	}
}

static void
xmouse2(Panel* p, Cmouse* m, Channel* mc, Point xy, int pos)
{
	int	updated;
	int	old;

	updated = 0;
	old = pos;
	for(;;){
		recv(mc, m);
		xy = m->xy;
		panelok(p);
		switch(m->buttons){
		case 0:
			run(p, updated ? p->ss : pos, 0);
			return;
		case 3:		// 2-1 chord
			run(p, pos, 1);
			return;
		case 2:
			if (!updated++)
				p->ss = p->se = p->s0 = pos;	
			pos = p->froff + frcharofpt(&p->f, xy);
			if (old == pos)
				sleep(10);
			else {
				setsel(p, p->s0, pos);
				drawsel(p);
				flushimage(display, 1);
			}
			break;
		}
	}
}

static void
xmouse3(Panel* p, Cmouse* m, Channel* mc, Point xy, int pos)
{
	double	jfactor;
	int	jpos, jy, old;
	Image*	i;

	recv(mc, m);
	xy = m->xy;
	switch(m->buttons){
	case 0:
		look(p, pos);
		drawsel(p);
		flushimage(display, 1);
		break;
	case 4:		// slide
		i = scrdraw(p, nil);
		flushimage(display, 1);
		jfactor = 0;
		jy = xy.y;
		old = p->froff;
		do {
			panelok(p);
			if (jfactor == 0){
				pos = p->froff;
				jfactor = jumpscale(p, xy);
			}
			jpos = pos + (xy.y - jy) * jfactor;
			if (jpos == old || !gotopos(p, jpos))
				sleep(10);
			else {
				old = jpos;
				scrdraw(p, i);;
				flushimage(display, 1);
			}
			recv(mc, m);
			xy = m->xy;
		} while(m->buttons);
		scrdone(p, i);
		flushimage(display, 1);
		break;
	case 5:		// 3-1 chord
		pos = p->froff + frcharofpt(&p->f, xy);
		if (pos < p->ss)
			p->ss = pos;
		else if (pos > p->se)
			p->se = pos;
		drawsel(p);
		flushimage(display, 1);
		do {
			recv(mc, m);
		} while (m->buttons);
		break;
	}
}

static void
xmouse(Panel* p, Cmouse* m, Channel* mc)
{
	int	pos;
	Point	xy;

	panelok(p);
	if ((p->flags&Pdead) || hidden(p->file)) // paranoia
		return;
	if (Dx(p->rect) <= 0 || Dy(p->rect) <= 0) // more paranoia
		return;
	xy = m->xy;
	pos = p->froff + frcharofpt(&p->f, xy);

	newedit(p);
	switch(m->buttons){
	case 1:
		xmouse1(p, m, mc, xy, pos);
		break;
	case 2:
		xmouse2(p, m, mc, xy, pos);
		break;
	case 4:
		xmouse3(p, m, mc, xy, pos);
		break;
	}
}

static void
sendkeys(Panel* p, int ss, int se)
{
	Tblock*	b;
	int	n;
	Rune*	r;

	if (se - ss <= 0)
		return;
	b = blockseek(p->blks, &n, ss);
	r = emalloc9p((se - ss + 1) * sizeof(Rune));
	blockget(b, n, se-ss, r);
	event(p, "keys %S", r);
	free(r);
}

static void
xkeyboard(Panel* p, Rune r)
{
	int	k = r;
	Rune	buf[2];
	int	pos;
	int	n;

	if ((p->flags&Pdead) || hidden(p->file))	// paranoia
		return;
	if (Dx(p->rect) <= 10 || Dy(p->rect) <= 10) // more paranoia
		return;
	pos = p->ss;
	switch(k){
	case Kbs:
		if (!(p->flags&Pedit)){
		kbevent:
			event(p, "keys %C", k);
			return;
		}
		if (pos <= 0){
			fdprint("top\n");
			return;
		}
		if (!cut(p, 1) && pos){
			pos--;
			mustshow(p, pos);
			textdel(p, buf, 1, pos);
			p->ss = p->se = pos;
			addedit(p, Del, buf, 1, pos);
		}
		dirty(p, 1, 1);
		filltext(p);
		break;
	case 0x17:	// ^W. erase word
	case 0x15:	// ^U. erase line
		if (!(p->flags&Pedit))
			goto kbevent;
		break;
	case Kdel:
		event(p, "interrupt");
		return;
	case Kleft:
		undocmd(p);
		break;
	case Kright:
		redocmd(p);
		break;
	case Kup:
	case Kdown:
		if (p->flags&Pline)
			goto kbevent;
		else {
			n = p->f.nlines / 3;
			if (n < 2)
				n = 2;
			if (scrollframe(p, (k == Kup) ? -n : n))
				xdraw(p, 0);
		}
		break;
	case Kesc:
		if (!(p->flags&Pedit))
			goto kbevent;
		setsel(p, p->s0, pos);
		drawsel(p);
		break;
	case '\n':
		if (p->flags&Pline){
			setsel(p, p->s0, pos);
			drawsel(p);
			run(p, (pos>0 ? pos-1 : 0), 0);
			break;
		}
		if (p->flags&Pedit)
			event(p, "dirty");
		// and fall...
	default:
		if (!(p->flags&Pedit))
			goto kbevent;
		cut(p, 1);
		mustshow(p, pos);
		if (textins(p, &r, 1, pos) && pos >= p->froff+p->f.nchars-2){
			p->ss = p->se = pos+1;
			scrollframe(p, gettextht(p)/3);
			xdraw(p, 0);
		} else
			p->ss = p->se = pos+1;
		addedit(p, Ins, &r, 1, pos);
		dirty(p, 1, 1);
		if ((p->flags&Pedit) && (r == '\n' || (p->flags&Pline)))
			settextsize(p);
	}
	flushimage(display, 1);
}

static long
xread(Panel* p, void* buf, long cnt, vlong off)
{
	char*	s;
	long	l;

	/* Would be better to have a real blockread here
	 * but this suffices.
	 */
	s = block2str(p->blks, &l);
	cnt = genreadbuf(buf, cnt, off, s, l);
	free(s);
	return cnt;
}

static int
validtextpos(Panel* p, int pos)
{
	int	l;

	l = blocklen(p->blks);
	if (pos < 0)
		pos = 0;
	if (pos > l)
		pos = l;
	return pos;
}

static long
xwrite(Panel* p, void* buf, long cnt, vlong off)
{
	int	pos;
	int	n;
	Rune*	r;
	int	nr;
	int	nout;
	Tblock*	b;
	Tblock*	nb;
	int	nl;
	char*	pb;

	/* Would be better to have a real blockwrite here
	 * but this suffices.
	 */
	assert(buf);
	pb = buf;
	if (off == 0LL)
		p->npartial = 0;
	if (p->npartial){
		if (p->partialoff == off){
			pb = emalloc9p(cnt + p->npartial);
			memmove(pb, p->partial, p->npartial);
			memmove(pb + p->npartial, buf, cnt);
		} else {
			p->npartial = 0;
			p->partialoff = 0;
			werrstr("partial rune");
			return -1;
		}
	}
	nout = p->npartial + cnt;
	r = utf2runes(pb, &nout, &nl);
	if (nout != p->npartial + cnt){
		n = p->npartial + cnt - nout;
		if (n >= UTFmax){	// binary file?
			p->npartial = 0;
			free(r);
			if (pb != buf)
				free(pb);
			werrstr("binary file?");
			return -1;
		}
		p->npartial = n;
		memmove(p->partial, pb + nout, p->npartial);
		p->partialoff = off + cnt;
	} else {
		p->npartial = 0;
		p->partialoff = 0;
	}
	if (pb != buf)
		free(pb);
	nr = runeslen(r);
	bdprint("data: %d runes [%.*S]\n", nr, nr, r);
	if (off == 0LL){
		pos = 0;
		blockfree(p->blks);
		p->blks = newblock(r, nr);
		p->nlines = nl;
	} else {
		pos = off2pos(p->blks, (long)off);
		b = blockseek(p->blks, &n, pos);
		b->nr = n;
		nb = newblock(r, nr);
		blockcat(b, nb);
		p->nlines += nl;
	}
	p->dfile->length = off+cnt;
	assert(pos + nr == blocklen(p->blks));
	pos = pos + nr;
	p->mark = validtextpos(p, pos);
	if (p->flags&Pline)
		pos = validtextpos(p, pos + nr - 10);
	p->ss = p->se = p->s0 = pos;
	settextsize(p);
	cleanedits(p);
	if (!hidden(p->file)){
		if (p->scroll)
			mustshow(p, pos);
		xdraw(p, 0);
		flushimage(display, 1);
	}
	return cnt;
}

static void
xtruncate(Panel* p)
{
	blockfree(p->blks);
	p->blks = newblock(nil, 0);
	p->nlines = 0;
	p->npartial = 0;
	p->mark = 0;
	p->dfile->length= 0LL;
	p->ss = p->se = p->s0 = 0;
	if (!hidden(p->file)){
		hidesel(p);
		xdraw(p, 0);
		flushimage(display, 1);
	}
}

static int
textctlwr(Panel* p, char* s)
{
	char*	q;
	char*	r;
	Rune*	rs;
	int	ss, se;
	int	pos, n;
	int	nl;
	int	mustdraw;

	mustdraw = 0;
	if (!strncmp(s, "ins ", 4)){
		pos = strtod(s + 4, &q);
		pos = validtextpos(p, pos);
		n = 0;
		rs = utf2runes(s + 4 + 11 + 1 + 11 + 1, &n, &nl);
		if (n>0){
			frameins(p, rs, n, pos);
			addedit(p, Ins, rs, n, pos);
			p->nlines += nl;
		}
		free(rs);
	} else if (!strncmp(s, "del ", 4)){
		pos = strtod(s + 4, &q);
		pos = validtextpos(p, pos);
		n = strtod(q, &r);
		if (n>0){
			rs = emalloc9p(sizeof(Rune)*(n+1));
			framedel(p, rs, n, pos);
			addedit(p, Del, rs, n, pos);
			p->nlines -= nlines(rs, n);
			free(rs);
		}
	} else if (!strncmp(s, "sel ", 4)){
		if (!strncmp(s + 4, "all", 3)){
			ss = 0;
			se = blocklen(p->blks);
		} else {
			ss = strtod(s + 4, &q);
			se = strtod(q, &r);
			ss = off2pos(p->blks, ss);
			se = off2pos(p->blks, se);
			ss = validtextpos(p, ss);
			se = validtextpos(p, se);
		}
		if (se < ss)
			se = ss;
		p->s0 = p->ss = ss;
		p->se = se;
		drawsel(p);
	} else if (!strncmp(s, "undo", 4)){
		if (!undocmd(p))
			return -1;
	} else if (!strncmp(s, "redo", 4)){
		if (!redocmd(p))
			return -1;
	} else if (!strncmp(s, "search ", 7)){
		search(p, s+7);
	} else if (!strcmp(s, "cut")){
		if (cut(p, 1)){
			settextsize(p);
			dirty(p, 1, 1);
		}
	} else if (!strcmp(s, "paste")){
		cut(p, 0);
		paste(p, p->ss);
		settextsize(p);
		dirty(p, 1, 1);
	} else if (!strncmp(s, "mark ", 5)){
		p->mark = off2pos(p->blks, atoi(s + 5));
		p->mark = validtextpos(p, p->mark);
	} else if (!strncmp(s, "exec ", 5)){
		if (!wcommand(s+5))
			event(p, "exec %11d %s", strlen(s+5), s+5);
	} else if (!strncmp(s, "look ", 5)){
		event(p, "look %11d %s", strlen(s+5), s+5);
	} else if (!strncmp(s, "scroll", 6))
		p->scroll = 1;
	else {
		werrstr("bad ctl");
		return -1;
	}
	setctlfilelen(p);
	return mustdraw;
}

int
gettextwid(Panel* p)
{
	return Dx(p->rect) / stringwidth(p->font, "X");
}

int
gettextht(Panel* p)
{
	if (p->f.maxlines && p->loaded)
		return p->f.maxlines;
	else
		return Dy(p->rect) / fontheight(p->font);
}

static long
xattrs(Panel* p, char* str, long l)
{
	char	sel[100];

	seprint(sel, sel+sizeof(sel),
		"mark %11ld\nsel %11ld %11ld\nsize %11d %11d\n%s",
		pos2off(p->blks, p->mark),
		pos2off(p->blks, p->ss), pos2off(p->blks, p->se),
		gettextwid(p), gettextht(p),
		(p->scroll ? "scroll\n" : ""));
	return sprintattrs(p, str, l, sel);
}


static int
xctl(Panel* p, char* ctl)
{
	int	r;
	int	odirty;
	Font*	of;

	of = p->font;
	odirty = p->flags&Pdirty;
	r = genctl(p, ctl);
	if (r < 0)
		r = textctlwr(p, ctl);
	else {
		if (odirty && !(p->flags&Pdirty))
			setnoedits(p);
		if (of != p->font){
			if (p->loaded){
				frclear(&p->f, 1);
				frinit(&p->f, p->rect, p->font, screen, cols);
			}
		}
	}
	if (r >= 0 && !hidden(p->file) && (p->flags&Pline) && settextsize(p))
		r = 1;
	return r;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.