Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/octopus/port/live/owpdraw.b

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


implement Pimpl;
include "mods.m";
mods, debug, win, tree: import dat;


Cpointer, cols, panelback, maxpt, cookclick, drawtag, TEXT,
BACK, HIGH, HBORD, BORD, HTEXT, SET, CLEAR, MBACK, MSET, MCLEAR, WS1,
WS2, WS3 : import gui;
Ptag, nth, Predraw, Panel: import wpanel;
panelctl, panelkbd, panelmouse, tagmouse, Tree: import wtree;

Pdraw: adt {
	canvas:	ref Image;
	dcmds:	string;
};

# Beware: Draw functions and colors must be kept in sync with
# ../mero/ompdraw.b, which checks out user writes for validity
# we recheck here, just in case.

Drawfunc: type ref fn(canvas: ref Image, args: list of string): Point;

Dcmd: adt {
	name: string;
	drawfn: Drawfunc;
};

Dcol: adt {
	name: string;
	img:	ref Image;
};

dcols: list of Dcol;
dcmds: array of Dcmd;
panels: array of ref Pdraw;

pimpl(p: ref Panel): ref Pdraw
{
	if (p.implid < 0 || p.implid > len panels || panels[p.implid] == nil)
		panic("draw: bug: no impl");
	return panels[p.implid];
}

initcols()
{
	dpy := win.display;
	icols := array[] of {
		("black", Draw->Black),
		("white", Draw->White),
		("red", Draw->Red),
		("green", Draw->Green),
		("blue", Draw->Blue),
		("cyan", Draw->Cyan),
		("magenta", Draw->Magenta),
		("yellow", Draw->Yellow),
		("grey", Draw->Grey),
		("paleyellow", Draw->Paleyellow),
		("darkyellow", Draw->Darkyellow),
		("darkgreen", Draw->Darkgreen),
		("palegreen", Draw->Palegreen),
		("medgreen", Draw->Medgreen),
		("darkblue", Draw->Darkblue),
		("palebluegreen", Draw->Palebluegreen),
		("paleblue", Draw->Paleblue),
		("bluegreen", Draw->Bluegreen),
		("greygreen", Draw->Greygreen),
		("palegreygreen", Draw->Palegreygreen),
		("yellowgreen", Draw->Yellowgreen),
		("medblue", Draw->Medblue),
		("greyblue", Draw->Greyblue),
		("palegreyblue", Draw->Palegreyblue),
		("purpleblue", Draw->Purpleblue),
	};
	dcols = nil;
	for (i := 0; i < len icols; i++)
		dcols = Dcol(icols[i].t0, dpy.color(icols[i].t1))::dcols;
	dcols = Dcol("back", cols[BACK]):: dcols;
	dcols = Dcol("high", cols[HIGH])::dcols;
	dcols = Dcol("bord", cols[BORD])::dcols;
	dcols = Dcol("text", cols[TEXT])::dcols;
	dcols = Dcol("htext", cols[HTEXT])::dcols;
	dcols = Dcol("hbord", cols[HBORD])::dcols;
	dcols = Dcol("set", cols[SET])::dcols;
	dcols = Dcol("clear", cols[CLEAR])::dcols;
	dcols = Dcol("mback", cols[MBACK])::dcols;
	dcols = Dcol("mset", cols[MSET])::dcols;
	dcols = Dcol("mclear", cols[MCLEAR])::dcols;
	dcols = Dcol("ws1", cols[WS1])::dcols;
	dcols = Dcol("ws2", cols[WS2])::dcols;
	dcols = Dcol("ws3", cols[WS3])::dcols;
}

init(d: Livedat): string
{
	prefixes = list of {"draw:"};
	dat = d;
	initmods();

	# BUG: needs arc fillarc, bezier, fillbezier, and text (from Draw->Image)
	# and probably needs move and copy.
	# PIC would be a nice language to implement here.

	dcmds = array[] of {
		Dcmd("ellipse", dellipse),		# ellipse cx cy rx ry  [w col]
		Dcmd("fillellipse", dfillellipse),	# fillellipse cx cy rx ry  [col]
		Dcmd("line", dline),			# line ax ay bx by [ea eb r col]
		Dcmd("rect", drect),			# rect ax ay bx by [col]
		Dcmd("poly", dpoly),			# poly x0 y0 x1 y1 ... xn yn e0 en w col
		Dcmd("bezspline", dpoly),		# bezspline x0 y0 x1 y1 ... xn yn e0 en w col
		Dcmd("fillpoly", dfillpoly),		# fillpoly x0 y0 x1 y1 ... xn yn w col
		Dcmd("fillbezspline", dfillpoly)	# fillbezspline x0 y0 x1 y1 ... xn yn w col
	};
	return nil;
}

pinit(p: ref Panel)
{
	if (tree == nil)
		tree = dat->tree;
	for (i := 0; i < len panels; i++)
		if (panels[i] == nil)
			break;
	if (i == len panels){
		npanels := array[i+16] of ref Pdraw;
		npanels[0:] = panels;
		panels = npanels;
	}
	p.implid = i;
	panels[i] = ref Pdraw(nil, "");
	p.minsz = Point(48,48);
}

pterm(p: ref Panel)
{
	if (p.implid != -1){
		pimpl(p);	# check
		panels[p.implid] = nil;
		p.implid = -1;
	}
}

pctl(p: ref Panel, s: string)
{
	panelctl(tree, p, s);
}

pevent(nil: ref Panel, nil: string)
{
	# no ins/del events
}

atocol(s: string): ref Image
{
	for(l := dcols; l != nil; l = tl l)
		if ((hd l).name == s)
			return (hd l).img;
	fprint(stderr, "o/live: atocol unknown color %s\n", s);
	return cols[TEXT];
}

dellipse(canvas: ref Image, args: list of string): Point
{
	if (len args < 5 || len args > 7)
		return Point(0,0);
	pt := Point(int nth(args, 1), int nth(args, 2));
	rd := Point(int nth(args, 3), int nth(args, 4));
	c := cols[TEXT];
	wid := 0;
	if (len args >= 6)
		wid = int nth(args, 5);
	if (len args == 7)
		c = atocol(nth(args, 6));
	if (canvas != nil)
		canvas.ellipse(pt, rd.x, rd.y, wid, c, (0,0));
	return pt.add(rd);
}

dfillellipse(canvas: ref Image, args: list of string): Point
{
	if (len args < 5 || len args > 6)
		return Point(0,0);
	pt := Point(int nth(args, 1), int nth(args, 2));
	rd := Point(int nth(args, 3), int nth(args, 4));
	c := cols[TEXT];
	if (len args == 6)
		c = atocol(nth(args, 5));
	if (canvas != nil)
			canvas.fillellipse(pt, rd.x, rd.y, c, (0,0));
	return pt.add(rd);
}

dline(canvas: ref Image, args: list of string): Point
{
	if (len args < 5 || len args > 9)
		return Point(0,0);
	min := Point(int nth(args, 1), int nth(args, 2));
	max := Point(int nth(args, 3), int nth(args, 4));
	ea := eb := wid := 0;
	c := cols[TEXT];
	if (len args >= 6)
		ea = int nth(args, 5);
	if (len args >= 7)
		eb = int nth(args, 6);
	if (len args >= 8)
		wid = int nth(args, 7);
	if (len args >= 9)
		c = atocol(nth(args, 8));
	if (canvas != nil)
		canvas.line(min, max, ea, eb, wid, c, (0,0));
	r := Rect(min, max);
	r = r.canon();
	return r.max;
}

drect(canvas: ref Image, args: list of string): Point
{
	if (len args < 5 || len args > 6)
		return Point(0,0);
	min := Point(int nth(args, 1), int nth(args, 2));
	max := Point(int nth(args, 3), int nth(args, 4));
	r := Rect(min, max);
	col := cols[TEXT];
	if (len args == 6)
		col = atocol(nth(args, 5));
	if (canvas != nil)
		canvas.draw(r, col, nil, (0,0));
	return max;
}

dpoly(canvas: ref Image, args: list of string): Point
{
	l := len args;
	if (l < 5 + 3 * 2)
		return Point(0,0);
	np := (l -5)/2;
	pts := array[np] of Point;
	cmd := hd args;
	args = tl args;
	max := Point(0,0);
	for(i := 0; i < len pts; i++){
		pts[i] = Point(int hd args, int hd tl args);
		args = tl tl args;
		if (max.x < pts[i].x)
			max.x = pts[i].x;
		if (max.y < pts[i].y)
			max.y = pts[i].y;
	}
	e0 := int hd args; args = tl args;
	e1 := int hd args; args = tl args;
	w  := int hd args; args = tl args;
	col  := atocol(hd args);
	if (canvas != nil)
		case cmd {
		"bezspline" =>
			canvas.bezspline(pts, e0, e1, w, col, (0,0));
		* =>
			canvas.poly(pts, e0, e1, w, col, (0,0));
		}
	return max;
}

dfillpoly(canvas: ref Image, args: list of string): Point
{
	l := len args;
	if (l < 3 + 3 * 2)
		return Point(0,0);
	np := (l - 3)/2;
	pts := array[np] of Point;
	cmd := hd args;
	args = tl args;
	max := Point(0,0);
	for(i := 0; i < len pts; i++){
		pts[i] = Point(int hd args, int hd tl args);
		args = tl tl args;
		if (max.x < pts[i].x)
			max.x = pts[i].x;
		if (max.y < pts[i].y)
			max.y = pts[i].y;
	}
	w  := int hd args; args = tl args;
	col  := atocol(hd args);
	if (canvas != nil)
		case cmd {
		"fillbezspline" =>
			canvas.fillbezspline(pts, w, col, (0,0));
		* =>
			canvas.fillpoly(pts, w, col, (0,0));
		}
	return max;
}

drawcmds(s: string, canvas: ref Image): Point
{
	max := Point(0,0);
	(nil, cmds) := tokenize(s, "\n");
	for(; cmds != nil; cmds = tl cmds){
		(nargs, args) := tokenize(hd cmds, " \t");
		if (nargs > 0){
			for (i := 0; i < len dcmds; i++)
				if (dcmds[i].name == hd args){
					if (canvas != nil && debug['D']){
						for(l := args; l != nil; l = tl l)
							fprint(stderr, "'%s' ", hd l);
						fprint(stderr, "\n");
					}
					x := dcmds[i];
					p := x.drawfn(canvas, args);
					max = maxpt(max, p);
					break;
				}
		}
	}
	max = max.add((2,2));				# two extra pixels
	max = maxpt(max, Point(10, 10));	# and ensure a min. sz
	return max;
}

pupdate(p: ref Panel, d: array of byte)
{
	if (dcols == nil)
		initcols();
	dpy := win.display;
	c := win.image.chans;
	pi := pimpl(p);
	if (pi == nil)
		return;
	ncmds := string d;
	if (pi.dcmds == ncmds)
		return;
	pi.dcmds = string d;
	if (pi.canvas != nil)
		pi.canvas = nil;
	p.minsz = drawcmds(pi.dcmds, nil);	# compute size
	r := Rect((0,0), p.minsz);
	cback := panelback(p.parent);
	ncanvas := dpy.newimage(r, c, 0, Draw->White);
	if (ncanvas == nil){
		fprint(stderr, "o/live: pdraw: %r\n");
		return;
	}
	ncanvas.draw(ncanvas.r, cback, nil, (0,0));
	drawcmds(pi.dcmds, ncanvas);
	pi.canvas = ncanvas;
	p.flags |= Predraw;
}

pdraw(p: ref Panel)
{
	if (dcols == nil)
		initcols();
	pi := pimpl(p);
	cback := panelback(p);
	if (pi != nil && pi.canvas != nil){
		win.image.draw(p.rect, pi.canvas, nil, (0,0));
		if (pi.canvas.r.dx() < p.rect.dx()){
			r := p.rect;
			r.min.x += pi.canvas.r.dx();
			win.image.draw(r, cback, nil, (0,0));
		}
		if (pi.canvas.r.dy() < p.rect.dy()){
			r := p.rect;
			r.min.y += pi.canvas.r.dy();
			win.image.draw(r, cback, nil, (0,0));
		}
	} else
		win.image.draw(p.rect, cback, nil, (0,0));
	if (p.flags&Ptag)
		drawtag(p);
}

pmouse(p: ref Panel, m: ref Cpointer, mc: chan of ref Cpointer)
{
	if (panelmouse(tree, p, m, mc))
		return;
	pt := m.xy.sub(p.rect.min);
	b := m.buttons;
	if (m.buttons != 0 && cookclick(m, mc))
		p.fsctl(sprint("click %d %d %d %d\n", pt.x, pt.y, b, m.msec), 1);
}

pkbd(p: ref Panel, r: int)
{
	panelkbd(nil, p, r);
}

psync(nil: ref Panel)
{
	# can't edit, no need to sync
}

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.