ref: db0eab9ca04b896e5760081efd854c57f65f36c3
dir: /libtk/canvu.c/
#include <lib9.h>
#include <kernel.h>
#include "draw.h"
#include "tk.h"
#include "canvs.h"
char*
tkparsepts(TkTop *t, TkCpoints *i, char **arg, int close)
{
char *s, *e;
Point *p, *d;
int n, npoint;
i->parampt = nil;
i->drawpt = nil;
i->bb = bbnil;
s = *arg;
npoint = 0;
while(*s) {
s = tkskip(s, " \t");
if(*s == '-' && (s[1] < '0' || s[1] > '9'))
break;
while(*s && *s != ' ' && *s != '\t')
s++;
npoint++;
}
i->parampt = mallocz(npoint*sizeof(Point), 0);
if(i->parampt == nil)
return TkNomem;
s = *arg;
p = i->parampt;
npoint = 0;
while(*s) {
e = tkfracword(t, &s, &p->x, nil);
if(e != nil)
goto Error;
e = tkfracword(t, &s, &p->y, nil);
if(e != nil)
goto Error;
npoint++;
s = tkskip(s, " \t");
if(*s == '-' && (s[1] < '0' || s[1] > '9'))
break;
p++;
}
*arg = s;
close = (close != 0);
i->drawpt = mallocz((npoint+close)*sizeof(Point), 0);
if(i->drawpt == nil){
e = TkNomem;
goto Error;
}
d = i->drawpt;
p = i->parampt;
for(n = 0; n < npoint; n++) {
d->x = TKF2I(p->x);
d->y = TKF2I(p->y);
if(d->x < i->bb.min.x)
i->bb.min.x = d->x;
if(d->x > i->bb.max.x)
i->bb.max.x = d->x;
if(d->y < i->bb.min.y)
i->bb.min.y = d->y;
if(d->y > i->bb.max.y)
i->bb.max.y = d->y;
d++;
p++;
}
if (close)
*d = i->drawpt[0];
i->npoint = npoint;
return nil;
Error:
tkfreepoint(i);
i->parampt = nil;
i->drawpt = nil;
return e;
}
TkCitem*
tkcnewitem(Tk *tk, int t, int n)
{
TkCitem *i;
i = malloc(n);
if(i == nil)
return nil;
memset(i, 0, n);
i->type = t;
i->env = tk->env;
i->env->ref++;
return i;
}
/*
* expand the canvas's dirty rectangle, clipping
* appropriately to its boundaries.
*/
void
tkcvssetdirty(Tk *tk)
{
TkCanvas *c;
Rectangle r;
c = TKobj(TkCanvas, tk);
r = tkrect(tk, 0);
if (rectclip(&r, rectsubpt(c->update, c->view)))
combinerect(&tk->dirty, r);
}
void
tkxlatepts(Point *p, int npoints, int x, int y)
{
while(npoints--) {
p->x += x;
p->y += y;
p++;
}
}
void
tkbbmax(Rectangle *bb, Rectangle *r)
{
if(r->min.x < bb->min.x)
bb->min.x = r->min.x;
if(r->min.y < bb->min.y)
bb->min.y = r->min.y;
if(r->max.x > bb->max.x)
bb->max.x = r->max.x;
if(r->max.y > bb->max.y)
bb->max.y = r->max.y;
}
void
tkpolybound(Point *p, int n, Rectangle *r)
{
while(n--) {
if(p->x < r->min.x)
r->min.x = p->x;
if(p->y < r->min.y)
r->min.y = p->y;
if(p->x > r->max.x)
r->max.x = p->x;
if(p->y > r->max.y)
r->max.y = p->y;
p++;
}
}
/*
* look up a tag for a canvas item.
* if n is non-nil, and the tag isn't found,
* then add it to the canvas's taglist.
* NB if there are no binds done on the
* canvas, these tags never get cleared out,
* even if nothing refers to them.
*/
TkName*
tkctaglook(Tk* tk, TkName *n, char *name)
{
ulong h;
TkCanvas *c;
char *p, *s;
TkName *f, **l;
c = TKobj(TkCanvas, tk);
s = name;
if(s == nil)
s = n->name;
if(strcmp(s, "current") == 0)
return c->current;
h = 0;
for(p = s; *p; p++)
h += 3*h + *p;
l = &c->thash[h%TkChash];
for(f = *l; f; f = f->link)
if(strcmp(f->name, s) == 0)
return f;
if(n == nil)
return nil;
n->link = *l;
*l = n;
return n;
}
char*
tkcaddtag(Tk *tk, TkCitem *i, int new)
{
TkCtag *t;
TkCanvas *c;
char buf[16];
TkName *n, *f, *link;
c = TKobj(TkCanvas, tk);
if(new != 0) {
i->id = ++c->id;
snprint(buf, sizeof(buf), "%d", i->id);
n = tkmkname(buf);
if(n == nil)
return TkNomem;
n->link = i->tags;
i->tags = n;
}
for(n = i->tags; n; n = link) {
link = n->link;
f = tkctaglook(tk, n, nil);
if(n != f)
free(n);
for(t = i->stag; t; t = t->itemlist)
if(t->name == f)
break;
if(t == nil) {
t = malloc(sizeof(TkCtag));
if(t == nil) {
tkfreename(link);
return TkNomem;
}
t->name = f;
t->taglist = f->obj; /* add to head of items with this tag */
f->obj = t;
t->item = i;
t->itemlist = i->stag; /* add to head of tags for this item */
i->stag = t;
}
}
i->tags = nil;
if(new != 0) {
i->tags = tkmkname("all");
if(i->tags == nil)
return TkNomem; /* XXX - Tad: memory leak? */
return tkcaddtag(tk, i, 0);
}
return nil;
}
void
tkfreepoint(TkCpoints *p)
{
free(p->drawpt);
free(p->parampt);
}
/*
* of all the items in ilist tagged with tag,
* return that tag for the first (topmost) item.
*/
TkCtag*
tkclasttag(TkCitem *ilist, TkCtag* tag)
{
TkCtag *last, *t;
if (tag == nil || tag->taglist == nil)
return tag;
last = nil;
while(ilist) {
for(t = tag; t; t = t->taglist) {
if(t->item == ilist) {
last = t;
break;
}
}
ilist = ilist->next;
}
return last;
}
/*
* of all the items in ilist tagged with tag,
* return that tag for the first (bottommost) item.
*/
TkCtag*
tkcfirsttag(TkCitem *ilist, TkCtag* tag)
{
TkCtag *t;
if (tag == nil || tag->taglist == nil)
return tag;
for (; ilist != nil; ilist = ilist->next)
for(t = tag; t; t = t->taglist)
if(t->item == ilist)
return t;
return nil;
}
void
tkmkpen(Image **pen, TkEnv *e, Image *stipple)
{
int locked;
Display *d;
Image *new, *fill;
fill = tkgc(e, TkCfill);
d = e->top->display;
locked = lockdisplay(d);
if(*pen != nil) {
freeimage(*pen);
*pen = nil;
}
if(stipple == nil) {
if(locked)
unlockdisplay(d);
return;
}
if(fill == nil)
fill = d->black;
new = allocimage(d, stipple->r, RGBA32, 1, DTransparent); /* XXX RGBA32 is excessive sometimes... */
if (new != nil)
draw(new, stipple->r, fill, stipple, ZP);
else
new = fill;
if(locked)
unlockdisplay(d);
*pen = new;
}
Point
tkcvsanchor(Point dp, int w, int h, int anchor)
{
Point o;
if(anchor & Tknorth)
o.y = dp.y;
else if(anchor & Tksouth)
o.y = dp.y - h;
else
o.y = dp.y - h/2;
if(anchor & Tkwest)
o.x = dp.x;
else if(anchor & Tkeast)
o.x = dp.x - w;
else
o.x = dp.x - w/2;
return o;
}
static TkCitem*
tkcvsmousefocus(TkCanvas *c, Point p)
{
TkCitem *i, *s;
int (*hit)(TkCitem*, Point);
if (c->grab != nil)
return c->grab;
s = nil;
for(i = c->head; i; i = i->next)
if(ptinrect(p, i->p.bb)) {
if ((hit = tkcimethod[i->type].hit) != nil && !(*hit)(i, p))
continue;
s = i;
}
return s;
}
Tk*
tkcvsinwindow(Tk *tk, Point *p)
{
TkCanvas *c;
TkCitem *i;
Point q;
TkCwind *w;
c = TKobj(TkCanvas, tk);
q = addpt(*p, c->view);
i = tkcvsmousefocus(c, addpt(*p, c->view));
if (i == nil || i->type != TkCVwindow)
return tk;
w = TKobj(TkCwind, i);
if (w->sub == nil)
return tk;
p->x = q.x - (i->p.bb.min.x + w->sub->borderwidth);
p->y = q.y - (i->p.bb.min.y + w->sub->borderwidth);
return w->sub;
}
static Tk*
tkcvsmouseinsub(TkCwind *w, TkMouse m)
{
Point g, mp;
int bd;
g = tkposn(w->sub);
bd = w->sub->borderwidth;
mp.x = m.x - (g.x + bd);
mp.y = m.y - (g.y + bd);
return tkinwindow(w->sub, mp, 0);
}
static Tk*
tkcvsdeliver(Tk *tk, TkCitem *i, int event, void *data)
{
Tk *ftk, *dest;
TkCtag *t;
TkCwind *w;
TkAction *a;
if(i->type == TkCVwindow) {
dest = nil;
w = TKobj(TkCwind, i);
if(w->sub == nil)
return nil;
if(!(event & TkKey) && (event & TkEmouse)) {
ftk = tkcvsmouseinsub(w, *(TkMouse*)data);
if(ftk != w->focus) {
tkdeliver(w->focus, TkLeave, data);
if(0)print("focus %p %q %p %q\n", w->sub, tkname(w->sub), ftk, tkname(ftk));
tkdeliver(ftk, TkEnter, data);
w->focus = ftk;
}
if(ftk != nil)
dest = tkdeliver(ftk, event, data);
} else {
if(event & TkLeave) {
tkdeliver(w->focus, TkLeave, data);
w->focus = nil;
} else if(event & TkEnter) {
ftk = tkcvsmouseinsub(w, *(TkMouse*)data);
tkdeliver(ftk, TkEnter, data);
w->focus = ftk;
} else
dest = tkdeliver(w->sub, event, data);
}
return dest;
}
for(t = i->stag; t != nil; t = t->itemlist) {
a = t->name->prop.binds;
if(a != nil)
tksubdeliver(tk, a, event, data, 0);
}
return nil;
}
Tk*
tkcvsevent(Tk *tk, int event, void *data)
{
TkMouse m;
TkCitem *f;
Point mp, g;
TkCanvas *c;
Tk *dest;
c = TKobj(TkCanvas, tk);
if(event == TkLeave && c->mouse != nil) {
tkcvsdeliver(tk, c->mouse, TkLeave, data);
c->mouse = nil;
}
dest = nil;
if(!(event & TkKey) && (event & TkEmouse) || (event & TkEnter)) {
m = *(TkMouse*)data;
g = tkposn(tk);
mp.x = (m.x - g.x - tk->borderwidth) + c->view.x;
mp.y = (m.y - g.y - tk->borderwidth) + c->view.y;
f = tkcvsmousefocus(c, mp);
if(c->mouse != f) {
if(c->mouse != nil) {
tkcvsdeliver(tk, c->mouse, TkLeave, data);
c->current->obj = nil;
}
if(f != nil) {
c->current->obj = &c->curtag;
c->curtag.item = f;
tkcvsdeliver(tk, f, TkEnter, data);
}
c->mouse = f;
}
f = c->mouse;
if(f != nil && (event & TkEnter) == 0)
dest = tkcvsdeliver(tk, f, event, &m);
}
if(event & TkKey) {
f = c->focus;
if(f != nil)
tkcvsdeliver(tk, f, event, data);
}
if(dest == nil)
tksubdeliver(tk, tk->binds, event, data, 0);
return dest;
}
/*
* debugging
*/
void
tkcvsdump(Tk *tk)
{
TkCanvas *c;
TkCitem *it;
TkCwind *w;
char v1[Tkminitem], v2[Tkminitem];
int i;
if(tk == nil)
return;
c = TKobj(TkCanvas, tk);
tkfprint(v1, c->width);
tkfprint(v2, c->height);
print("%q configure -width %s -height %s", tkname(tk), v1, v2);
print(" # focus %#p mouse %#p grab %#p\n", c->focus, c->mouse, c->grab);
for(it = c->head; it != nil; it = it->next){
print("%q create %q", tkname(tk), tkcimethod[it->type].name);
for(i = 0; i < it->p.npoint; i++){
tkfprint(v1, it->p.parampt[i].x);
tkfprint(v2, it->p.parampt[i].y);
print(" %s %s", v1, v2);
}
if(it->type == TkCVwindow){
w = TKobj(TkCwind, it);
if(w->sub != nil)
print(" -window %q", tkname(w->sub));
print(" # item %#p id %d sub %#p focus [%#p %q]\n", it, it->id, w->sub, w->focus, tkname(w->focus));
}else
print("# item %#p id %d\n", it, it->id);
}
}