shithub: blie

ref: 13590f759547f4ef544b251f2966b4ed2d82038c
dir: /layer.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <event.h>
#include <bio.h>
#include "blie.h"

char Ebadfmt[] = "bad file format: ";

typedef struct List List;
struct List {
	Layer *layer;
	List *next;
};

List *firstlayer = nil;
int numlayers = 0;

static Drawop
str2op(char *s)
{
#define OP(n) if (!strcmp(s, "n")) { return n; }
	OP(Clear);
	OP(SinD);
	OP(DinS);
	OP(SoutD);
	OP(DoutS);
	OP(S);
	OP(SoverD);
	OP(SatopD);
	OP(SxorD);
	OP(D);
	OP(DoverS);
	OP(DatopS);
	OP(DxorS);
#undef OP
	return SoverD;
}

static char*
op2str(Drawop op)
{
#define OP(n) if (op == n) { return "n"; }
	OP(Clear);
	OP(SinD);
	OP(DinS);
	OP(SoutD);
	OP(DoutS);
	OP(S);
	OP(SoverD);
	OP(SatopD);
	OP(SxorD);
	OP(D);
	OP(DoverS);
	OP(DatopS);
	OP(DxorS);
#undef OP
	return "SoverD";
}

static void
_renum(Layer *l, int id, int, void*)
{
	l->num = id;
}

static void
renumlayers(void)
{
	foreachlayer(_renum, nil);
}

static void
addlayerl(Layer *l)
{
	List *f;
	if (!firstlayer) {
		firstlayer = mallocz(sizeof(List), 1);
		firstlayer->layer = l;
		l->num = 0;
		return;
	}
	for (f = firstlayer; f->next; f = f->next)
		continue;
	f->next = mallocz(sizeof(List), 1);
	f->next->layer = l;
	l->num = f->layer->num + 1;
	numlayers++;
}

void
addlayer(char *name)
{
	Biobuf *b;
	char *file;
	char *ed, *lbl, *op;
	Layer *l;
	
	if (!name || !name[0])
		return;
	
	file = smprint("l/%s/meta", name);
	b = Bopen(file, OREAD);
	free(file);
	if (!b)
		sysfatal("error opening file %s/meta: %r", name);
	
	ed = Brdstr(b, '\n', 1);
	if (!ed)
		sysfatal("%s%s/meta: missing editor", Ebadfmt, name);
	lbl = Brdstr(b, '\n', 1);
	if (!lbl)
		sysfatal("%s%s/meta: missing label", Ebadfmt, name);
	op = Brdstr(b, '\n', 1);
	if (!op)
		sysfatal("%s%s/meta: missing drawop", Ebadfmt, name);
	
	l = mallocz(sizeof(Layer), 1);
	l->name = strdup(name);
	l->label = strdup(lbl);
	l->editor = geteditor(ed);
	l->op = str2op(op);
	if (!l->editor)
		sysfatal("editor %s not found for %s", ed, name);
	addlayerl(l);
	free(lbl);
	free(ed);
	free(op);
	
	Bterm(b);
}

void
movelayer(Layer *layer, int dir)
{
	List *parent;
	List *l;
	
	if (firstlayer->layer == layer) {
		if (dir == DOWN) {
			l = firstlayer->next;
			firstlayer->next = l ? l->next : nil;
			l->next = firstlayer;
			firstlayer = l;
			return;
		}
		if (dir == UP)
			return; /* NOP */
	}
	
	/* find parent */
	for (l = firstlayer; l && l->next->layer != layer; l = l->next)
		continue;
	
	if (!l)
		return; /* not found */
	parent = l;
	
	if (dir == DOWN)
		goto Movedown;
	if (dir != UP)
		sysfatal("code error: direction != UP|DOWN");
	
	/* movelayer(layer, UP) == movelayer(parent, DOWN) */
	movelayer(parent->layer, DOWN);
	
	return;
Movedown:
	l = parent->next;
	if (!l)
		return; /* NOP: last element */
	parent->next = l->next;
	l->next = l->next ? l->next->next : nil;
	parent->next->next = l;
	
	// TODO: verify!
	l->layer->num--;
	if (l->next)
		l->next->layer->num++;
}

void
dirtylayer(Layer *layer)
{
	if (layer->composited)
		freememimage(layer->composited);
	layer->composited = nil;
}

void
savelayermeta(Layer *layer)
{
	Biobuf *b;
	char *file;
	
	file = smprint("l/%s/meta", layer->name);
	b = Bopen(file, OWRITE|OTRUNC);
	free(file);
	if (!b)
		sysfatal("error opening file %s.meta for write: %r", layer->name);
	
	Bprint(b, "%s\n%s\n", layer->editor->name, layer->label);
	Bterm(b);
}

int
foreachlayer(void (*f)(Layer*, int, int, void*), void* aux)
{
	List *l;
	int i;
	
	i = 0;
	for (l = firstlayer; l; l = l->next, i++) {
		f(l->layer, i, !l->next, aux);
	}
	
	return i;
}

int layerheight;

static void
redrawlayer(Layer *l, int n, int, void *aux)
{
	void **data;
	Image *img;
	Layer *active;
	Point p;
	Rectangle r, sr;
	
	data = (void**)aux;
	img = data[0];
	active = data[1];
	
	p = img->r.min;
	p.y += numlayers * layerheight;
	p.y -= n * layerheight;
	
	r.min = r.max = p;
	r.max.x += vdata.layerwinwidth;
	r.max.y += layerheight;
	sr.min = Pt(r.min.x, r.max.y);
	sr.max = r.max;
	
	if (l == active) {
		draw(img, r, vdata.gray, nil, ZP);
	}
	string(img, addpt(p, Pt(2, 2)), display->black, ZP, font, l->label);
	
	if (l == active && l->editor && l->editor->drawlwin) {
		r.min.y += vdata.fontheight;
		l->editor->drawlwin(l, img, r);
	}
	line(img, sr.min, sr.max, Endsquare, Endsquare, 0, display->black, ZP);
}

void
redrawlayers(Image *img, Layer *active)
{
	void *data[2];
	data[0] = img;
	data[1] = active;
	layerheight = vdata.fontheight + 20;
	draw(img, img->r, display->white, nil, ZP);
	foreachlayer(redrawlayer, data);
	line(img, img->r.min, Pt(img->r.min.x, img->r.max.y),
		Endsquare, Endsquare, 0, display->black, ZP);
}

static void
checkclick(Layer *l, int n, int, void *aux)
{
	void **a;
	void (*factivate)(Layer*);
	int *w;
	Image *img;
	Event *ev;
	Layer *active;
	Redrawwin *ret;
	Rectangle r;
	
	a = (void**)aux;
	factivate = a[0];
	w = a[1];
	img = a[2];
	ev = a[3];
	active = a[4];
	ret = a[5];
	
	USED(img);
	
	if (n != *w)
		return;
	
	if (l != active) {
		factivate(l);
		return;
	}
	r.min = r.max = ZP;
	r.max.x = vdata.layerwinwidth;
	r.min.y += numlayers * layerheight;
	r.min.y -= n * layerheight;
	r.max.y = r.min.y + layerheight;
	r.min.y += vdata.fontheight;
	
	if (l->editor && l->editor->lwininput) {
		if (ptinrect(ev->mouse.xy, r)) {
			ev->mouse.xy.x -= r.min.x;
			ev->mouse.xy.y -= r.min.y;
			*ret |= l->editor->lwininput(l, Emouse, *ev);
		}
	}
}

Redrawwin
clicklayer(Event ev, Layer *active, Image *img, void (*f)(Layer*))
{
	void *aux[6];
	int d;
	Redrawwin result;
	
	aux[0] = f;
	aux[1] = &d;
	aux[2] = img;
	aux[3] = &ev;
	aux[4] = active;
	aux[5] = &result;
	
	result = Rnil;
	d = numlayers - ev.mouse.xy.y / layerheight;
	
	foreachlayer(checkclick, aux);
	return result;
}