diff options
Diffstat (limited to 'libtk/windw.c')
| -rw-r--r-- | libtk/windw.c | 716 |
1 files changed, 716 insertions, 0 deletions
diff --git a/libtk/windw.c b/libtk/windw.c new file mode 100644 index 00000000..4c9e608c --- /dev/null +++ b/libtk/windw.c @@ -0,0 +1,716 @@ +#include "lib9.h" +#include "draw.h" +#include "tk.h" +#include "canvs.h" +#include "textw.h" +#include "kernel.h" + +TkCtxt* +tknewctxt(Display *d) +{ + TkCtxt *c; + c = malloc(sizeof(TkCtxt)); + if(c == nil) + return nil; + c->lock = libqlalloc(); + if(c->lock == nil){ + free(c); + return nil; + } + if (tkextnnewctxt(c) != 0) { + free(c->lock); + free(c); + return nil; + } + c->display = d; + return c; +} + +void +tkfreectxt(TkCtxt *c) +{ + int locked; + Display *d; + + if(c == nil) + return; + + tkextnfreectxt(c); + + d = c->display; + locked = lockdisplay(d); + tkfreecolcache(c); + freeimage(c->i); + freeimage(c->ia); + if(locked) + unlockdisplay(d); + libqlfree(c->lock); + free(c); +} + +Image* +tkitmp(TkEnv *e, Point p, int fillcol) +{ + Image *i, **ip; + TkTop *t; + TkCtxt *ti; + Display *d; + Rectangle r; + ulong pix; + int alpha; + + t = e->top; + ti = t->ctxt; + d = t->display; + + pix = e->colors[fillcol]; + alpha = (pix & 0xff) != 0xff; + ip = alpha ? &ti->ia : &ti->i; + + if(*ip != nil) { + i = *ip; + if(p.x <= i->r.max.x && p.y <= i->r.max.y) { + r.min = ZP; + r.max = p; + if (alpha) + drawop(i, r, nil, nil, ZP, Clear); + draw(i, r, tkgc(e, fillcol), nil, ZP); + return i; + } + r = i->r; + freeimage(i); + if(p.x < r.max.x) + p.x = r.max.x; + if(p.y < r.max.y) + p.y = r.max.y; + } + + r.min = ZP; + r.max = p; + *ip = allocimage(d, r, alpha?RGBA32:d->image->chan, 0, pix); + + return *ip; +} + +void +tkgeomchg(Tk *tk, TkGeom *g, int bd) +{ + int w, h; + void (*geomfn)(Tk*); + if(memcmp(&tk->req, g, sizeof(TkGeom)) == 0 && bd == tk->borderwidth) + return; + + geomfn = tkmethod[tk->type]->geom; + if(geomfn != nil) + geomfn(tk); + + if(tk->master != nil) { + tkpackqit(tk->master); + tkrunpack(tk->env->top); + } + else + if(tk->geom != nil) { + w = tk->req.width; + h = tk->req.height; + tk->req.width = 0; + tk->req.height = 0; + tk->geom(tk, tk->act.x, tk->act.y, w, h); + if (tk->slave) { + tkpackqit(tk); + tkrunpack(tk->env->top); + } + } + tkdeliver(tk, TkConfigure, g); +} + +/* + * return the widget within tk with by point p (in widget coords) + */ +Tk* +tkinwindow(Tk *tk, Point p, int descend) +{ + Tk *f; + Point q; + if (ptinrect(p, tkrect(tk, 1)) == 0) + return nil; + for (;;) { + if (descend && tkmethod[tk->type]->inwindow != nil) + f = tkmethod[tk->type]->inwindow(tk, &p); + else { + for (f = tk->slave; f; f = f->next) { + q.x = p.x - (f->act.x + f->borderwidth); + q.y = p.y - (f->act.y + f->borderwidth); + if (ptinrect(q, tkrect(f, 1))) + break; + } + p = q; + } + if (f == nil || f == tk) + return tk; + tk = f; + } + return nil; /* for compiler */ +} + +Tk* +tkfindfocus(TkTop *t, int x, int y, int descend) +{ + Point p, q; + Tk *tk, *f; + TkWin *tkw; + p.x = x; + p.y = y; + for(f = t->windows; f != nil; f = TKobj(TkWin, f)->next) { + assert(f->flag&Tkwindow); + if(f->flag & Tkmapped) { + tkw = TKobj(TkWin, f); + q.x = p.x - (tkw->act.x+f->borderwidth); + q.y = p.y - (tkw->act.y+f->borderwidth); + tk = tkinwindow(f, q, descend); + if(tk != nil) + return tk; + } + } + return nil; +} + +void +tkmovewin(Tk *tk, Point p) +{ + TkWin *tkw; + if((tk->flag & Tkwindow) == 0) + return; + tkw = TKobj(TkWin, tk); + if(! eqpt(p, tkw->req)){ + tkw->req = p; + tkw->changed = 1; + } +} + +void +tkmoveresize(Tk *tk, int x, int y, int w, int h) +{ + TkWin *tkw; + USED(x); + USED(y); + assert(tk->flag&Tkwindow); + tkw = TKobj(TkWin, tk); + if(w < 0) + w = 0; + if(h < 0) + h = 0; +//print("moveresize %s %d %d +[%d %d], callerpc %lux\n", tk->name->name, x, y, w, h, getcallerpc(&tk)); + tk->req.width = w; + tk->req.height = h; + tk->act = tk->req; + /* XXX perhaps should actually suspend the window here? */ + tkw->changed = 1; +} + +static void +tkexterncreatewin(Tk *tk, Rectangle r) +{ + TkWin *tkw; + TkTop *top; + char *name; + + top = tk->env->top; + tkw = TKobj(TkWin, tk); + + /* + * for a choicebutton menu, use the name of the choicebutton which created it + */ + if(tk->name == nil){ + name = tkw->cbname; + assert(name != nil); + } else + name = tk->name->name; + + tkw->reqid++; + tkwreq(top, "!reshape %s %d %d %d %d %d", name, tkw->reqid, r.min.x, r.min.y, r.max.x, r.max.y); + tkw->changed = 0; + tk->flag |= Tksuspended; +} + +/* + * return non-zero if the window size has changed (XXX choose better return value/function name!) + */ +int +tkupdatewinsize(Tk *tk) +{ + TkWin *tkw; + Image *previ; + Rectangle r, or; + int bw2; + + tkw = TKobj(TkWin, tk); + bw2 = 2*tk->borderwidth; + r.min.x = tkw->req.x; + r.min.y = tkw->req.y; + r.max.x = r.min.x + tk->act.width + bw2; + r.max.y = r.min.y + tk->act.height + bw2; + previ = tkw->image; + if(previ != nil){ + or.min.x = tkw->act.x; + or.min.y = tkw->act.y; + or.max.x = tkw->act.x + Dx(previ->r); + or.max.y = tkw->act.y + Dy(previ->r); + if(eqrect(or, r)) + return 0; + } + tkexterncreatewin(tk, r); + return 1; +} + +static char* +tkdrawslaves1(Tk *tk, Point orig, Image *dst, int *dirty) +{ + Tk *f; + char *e = nil; + Point worig; + Rectangle r, oclip; + + worig.x = orig.x + tk->act.x + tk->borderwidth; + worig.y = orig.y + tk->act.y + tk->borderwidth; + + r = rectaddpt(tk->dirty, worig); + if (Dx(r) > 0 && rectXrect(r, dst->clipr)) { + e = tkmethod[tk->type]->draw(tk, orig); + tk->dirty = bbnil; + *dirty = 1; + } + if(e != nil) + return e; + + /* + * grids need clipping + * XXX BUG: they can't, 'cos text widgets don't clip appropriately. + */ + if (tk->grid != nil) { + r = rectaddpt(tkrect(tk, 0), worig); + if (rectclip(&r, dst->clipr) == 0) + return nil; + oclip = dst->clipr; + replclipr(dst, 0, r); + } + for(f = tk->slave; e == nil && f; f = f->next) + e = tkdrawslaves1(f, worig, dst, dirty); + if (tk->grid != nil) + replclipr(dst, 0, oclip); + return e; +} + +char* +tkdrawslaves(Tk *tk, Point orig, int *dirty) +{ + Image *i; + char *e; + i = tkimageof(tk); + if (i == nil) + return nil; + e = tkdrawslaves1(tk, orig, i, dirty); + return e; +} + +char* +tkupdate(TkTop *t) +{ + Tk* tk; + int locked; + TkWin *tkw; + Display *d; + char *e; + int dirty = 0; + if(t->noupdate) + return nil; + + d = t->display; + locked = lockdisplay(d); + tk = t->windows; + while(tk) { + tkw = TKobj(TkWin, tk); + if((tk->flag & (Tkmapped|Tksuspended)) == Tkmapped) { + if (tkupdatewinsize(tk) == 0){ + e = tkdrawslaves(tk, ZP, &dirty); + if(e != nil) + return e; + } + } + tk = tkw->next; + } + if (dirty || t->dirty) { + flushimage(d, 1); + t->dirty = 0; + } + if(locked) + unlockdisplay(d); + return nil; +} + +int +tkischild(Tk *tk, Tk *child) +{ + while(child != nil && child != tk){ + if(child->master) + child = child->master; + else + child = child->parent; + } + return child == tk; +} + +void +tksetbits(Tk *tk, int mask) +{ + tk->flag |= mask; + for(tk = tk->slave; tk; tk = tk->next) + tksetbits(tk, mask); +} + +char* +tkmap(Tk *tk) +{ +/* + is this necessary? + tkw = TKobj(TkWin, tk); + if(tkw->image != nil) + tkwreq(tk->env->top, "raise %s", tk->name->name); +*/ + + if(tk->flag & Tkmapped) + return nil; + + tk->flag |= Tkmapped; + tkmoveresize(tk, 0, 0, tk->act.width, tk->act.height); + tkdeliver(tk, TkMap, nil); + return nil; +//tkupdate(tk->env->top); +} + +void +tkclrfocus(Tk *master) +{ + TkCtxt *c; + Tk *tk; + TkTop *top; + + if(master == nil) + return; + top = master->env->top; + c = top->ctxt; + + tk = c->mgrab; + if(tkischild(master, tk)) + tksetmgrab(top, nil); + + tk = c->entered; + if(tkischild(master, tk)){ + c->entered = nil; + tkdeliver(tk, TkLeave, nil); + } +} + +void +tkunmap(Tk *tk) +{ + TkTop *t; + TkCtxt *c; + + while(tk->master) + tk = tk->master; + + if((tk->flag & Tkmapped) == 0) + return; + + t = tk->env->top; + c = t->ctxt; + + if(tkischild(tk, c->mgrab)) + tksetmgrab(t, nil); + if(tkischild(tk, c->entered)){ + tkdeliver(c->entered, TkLeave, nil); + c->entered = nil; + } + if(tk == t->root) + tksetglobalfocus(t, 0); + + tk->flag &= ~(Tkmapped|Tksuspended); + + tkdestroywinimage(tk); + tkdeliver(tk, TkUnmap, nil); + tkenterleave(t); + /* XXX should unmap menus too */ +} + +Image* +tkimageof(Tk *tk) +{ + while(tk) { + if(tk->flag & Tkwindow) + return TKobj(TkWin, tk)->image; + if(tk->parent != nil) { + tk = tk->parent; + switch(tk->type) { + case TKmenu: + return TKobj(TkWin, tk)->image; + case TKcanvas: + return TKobj(TkCanvas, tk)->image; + case TKtext: + return TKobj(TkText, tk)->image; + } + abort(); + } + tk = tk->master; + } + return nil; +} + +void +tktopopt(Tk *tk, char *opt) +{ + TkTop *t; + TkWin *tkw; + TkOptab tko[4]; + + tkw = TKobj(TkWin, tk); + + t = tk->env->top; + + tko[0].ptr = tkw; + tko[0].optab = tktop; + tko[1].ptr = tk; + tko[1].optab = tkgeneric; + tko[2].ptr = t; + tko[2].optab = tktopdbg; + tko[3].ptr = nil; + + tkparse(t, opt, tko, nil); +} + +/* general compare - compare top-left corners, y takes priority */ +static int +tkfcmpgen(void *ap, void *bp) +{ + TkWinfo *a = ap, *b = bp; + + if (a->r.min.y > b->r.min.y) + return 1; + if (a->r.min.y < b->r.min.y) + return -1; + if (a->r.min.x > b->r.min.x) + return 1; + if (a->r.min.x < b->r.min.x) + return -1; + return 0; +} + +/* compare x-coords only */ +static int +tkfcmpx(void *ap, void *bp) +{ + TkWinfo *a = ap, *b = bp; + return a->r.min.x - b->r.min.x; +} + +/* compare y-coords only */ +static int +tkfcmpy(void *ap, void *bp) +{ + TkWinfo *a = ap, *b = bp; + return a->r.min.y - b->r.min.y; +} + +static void +tkfintervalintersect(int min1, int max1, int min2, int max2, int *min, int *max) +{ + if (min1 < min2) + min1 = min2; + if (max1 > max2) + max1 = max2; + if (max1 > min1) { + *min = min1; + *max = max1; + } else + *max = *min; /* no intersection */ +} + +void +tksortfocusorder(TkWinfo *inf, int n) +{ + int i; + Rectangle overlap, r; + int (*cmpfn)(void*, void*); + + overlap = inf[0].r; + for (i = 0; i < n; i++) { + r = inf[i].r; + tkfintervalintersect(overlap.min.x, overlap.max.x, + r.min.x, r.max.x, &overlap.min.x, &overlap.max.x); + tkfintervalintersect(overlap.min.y, overlap.max.y, + r.min.y, r.max.y, &overlap.min.y, &overlap.max.y); + } + + if (Dx(overlap) > 0) + cmpfn = tkfcmpy; + else if (Dy(overlap) > 0) + cmpfn = tkfcmpx; + else + cmpfn = tkfcmpgen; + + qsort(inf, n, sizeof(*inf), cmpfn); +} + +void +tkappendfocusorder(Tk *tk) +{ + TkTop *tkt; + tkt = tk->env->top; + if (tk->flag & Tktakefocus) + tkt->focusorder[tkt->nfocus++] = tk; + if (tkmethod[tk->type]->focusorder != nil) + tkmethod[tk->type]->focusorder(tk); +} + +void +tkbuildfocusorder(TkTop *tkt) +{ + Tk *tk; + int n; + + if (tkt->focusorder != nil) + free(tkt->focusorder); + n = 0; + for (tk = tkt->root; tk != nil; tk = tk->siblings) + if (tk->flag & Tktakefocus) + n++; + if (n == 0) { + tkt->focusorder = nil; + return; + } + + tkt->focusorder = malloc(sizeof(*tkt->focusorder) * n); + tkt->nfocus = 0; + if (tkt->focusorder == nil) + return; + + tkappendfocusorder(tkt->root); +} + +void +tkdirtyfocusorder(TkTop *tkt) +{ + free(tkt->focusorder); + tkt->focusorder = nil; + tkt->nfocus = 0; +} + +#define O(t, e) ((long)(&((t*)0)->e)) +#define OA(t, e) ((long)(((t*)0)->e)) + +typedef struct TkSee TkSee; +struct TkSee { + int r[4]; + int p[2]; + int query; +}; + +static +TkOption seeopts[] = { + "rectangle", OPTfrac, OA(TkSee, r), IAUX(4), + "point", OPTfrac, OA(TkSee, p), IAUX(2), + "where", OPTbool, O(TkSee, query), nil, + nil +}; + +char* +tkseecmd(TkTop *t, char *arg, char **ret) +{ + TkOptab tko[2]; + TkSee opts; + TkName *names; + Tk *tk; + char *e; + Rectangle vr; + Point vp; + + opts.r[0] = bbnil.min.x; + opts.r[1] = bbnil.min.y; + opts.r[2] = bbnil.max.x; + opts.r[3] = bbnil.max.y; + opts.p[0] = bbnil.max.x; + opts.p[1] = bbnil.max.y; + opts.query = 0; + + tko[0].ptr = &opts; + tko[0].optab = seeopts; + tko[1].ptr = nil; + names = nil; + e = tkparse(t, arg, tko, &names); + if (e != nil) + return e; + if (names == nil) + return TkBadwp; + tk = tklook(t, names->name, 0); + tkfreename(names); + if (tk == nil) + return TkBadwp; + if (opts.query) { + if (!tkvisiblerect(tk, &vr)) + return nil; + /* XXX should this be converted into screen coords? */ + return tkvalue(ret, "%d %d %d %d", vr.min.x, vr.min.y, vr.max.x, vr.max.y); + } + vr.min.x = opts.r[0]; + vr.min.y = opts.r[1]; + vr.max.x = opts.r[2]; + vr.max.y = opts.r[3]; + vp.x = opts.p[0]; + vp.y = opts.p[1]; + + if (eqrect(vr, bbnil)) + vr = tkrect(tk, 1); + if (eqpt(vp, bbnil.max)) + vp = vr.min; + tksee(tk, vr, vp); + return nil; +} + +/* + * make rectangle r in widget tk visible if possible; + * if not possible, at least make point p visible. + */ +void +tksee(Tk *tk, Rectangle r, Point p) +{ + Point g; +//print("tksee %R, %P in %s\n", r, p, tk->name->name); + g = Pt(tk->borderwidth, tk->borderwidth); + if(tk->parent != nil) { + g = addpt(g, tkmethod[tk->parent->type]->relpos(tk)); + tk = tk->parent; + } else { + g.x += tk->act.x; + g.y += tk->act.y; + tk = tk->master; + } + r = rectaddpt(r, g); + p = addpt(p, g); + while (tk != nil) { + if (tkmethod[tk->type]->see != nil){ +//print("see r %R, p %P in %s\n", r, p, tk->name->name); + tkmethod[tk->type]->see(tk, &r, &p); +//print("now r %R, p %P\n", r, p); + } + g = Pt(tk->borderwidth, tk->borderwidth); + if (tk->parent != nil) { + g = addpt(g, tkmethod[tk->parent->type]->relpos(tk)); + tk = tk->parent; + } else { + g.x += tk->act.x; + g.y += tk->act.y; + tk = tk->master; + } + r = rectaddpt(r, g); + p = addpt(p, g); + } +} |
