summaryrefslogtreecommitdiff
path: root/libtk/panel.c
diff options
context:
space:
mode:
Diffstat (limited to 'libtk/panel.c')
-rw-r--r--libtk/panel.c408
1 files changed, 408 insertions, 0 deletions
diff --git a/libtk/panel.c b/libtk/panel.c
new file mode 100644
index 00000000..f482a796
--- /dev/null
+++ b/libtk/panel.c
@@ -0,0 +1,408 @@
+#include "lib9.h"
+#include "draw.h"
+#include "tk.h"
+
+#define O(t, e) ((long)(&((t*)0)->e))
+
+typedef struct TkPanel TkPanel;
+struct TkPanel
+{
+ Image* image;
+ Image* matte;
+ Point view; /* vector from image origin to widget origin */
+ Rectangle r; /* drawn rectangle (in image coords) */
+ int anchor;
+ int hasalpha; /* does the image include an alpha channel? */
+};
+
+static TkOption tkpanelopts[] =
+{
+ "anchor", OPTflag, O(TkPanel, anchor), tkanchor,
+ nil
+};
+
+static int
+tkdrawnrect(Image *image, Image *matte, Rectangle *r)
+{
+ *r = image->clipr;
+ if (matte != nil) {
+ if (!rectclip(r, matte->clipr))
+ return 0;
+ if (!matte->repl && !rectclip(r, matte->r))
+ return 0;
+ }
+ if (!image->repl && !rectclip(r, image->r))
+ return 0;
+ return 1;
+}
+
+char*
+tkpanel(TkTop *t, char *arg, char **ret)
+{
+ TkOptab tko[3];
+ Tk *tk;
+ TkPanel *tkp;
+ TkName *names;
+ char *e;
+
+ tk = tknewobj(t, TKpanel, sizeof(Tk)+sizeof(TkPanel));
+ if(tk == nil)
+ return TkNomem;
+
+ tkp = TKobj(TkPanel, tk);
+ tkp->anchor = Tkcenter;
+
+ tko[0].ptr = tk;
+ tko[0].optab = tkgeneric;
+ tko[1].ptr = tkp;
+ tko[1].optab = tkpanelopts;
+ tko[2].ptr = nil;
+ names = nil;
+
+ e = tkparse(t, arg, tko, &names);
+ if(e != nil) {
+ tkfreeobj(tk);
+ return e;
+ }
+
+ tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
+
+ e = tkaddchild(t, tk, &names);
+
+ tkfreename(names);
+ if (e != nil) {
+ tkfreeobj(tk);
+ return e;
+ }
+
+ tk->name->link = nil;
+ return tkvalue(ret, "%s", tk->name->name);
+}
+
+void
+tkgetpanelimage(Tk *tk, Image **i, Image **m)
+{
+ TkPanel *tkp = TKobj(TkPanel, tk);
+ *i = tkp->image;
+ *m = tkp->matte;
+}
+
+void
+tksetpanelimage(Tk *tk, Image *image, Image *matte)
+{
+ TkPanel *tkp = TKobj(TkPanel, tk);
+ int ishuge;
+ TkGeom g;
+
+ g = tk->req;
+
+ tkp->image = image;
+ tkp->matte = matte;
+
+ if (!tkdrawnrect(image, matte, &tkp->r)) {
+ tkp->r.min = image->r.min;
+ tkp->r.max = image->r.min;
+ }
+
+ tkp->view = tkp->r.min; /* XXX do we actually want to keep the old one? */
+ /*
+ * if both image and matte are replicated, then we've got no idea what
+ * the rectangle should be, so request zero size, and set origin to (0, 0).
+ */
+ ishuge = (Dx(tkp->r) >= 10000000);
+ if((tk->flag & Tksetwidth) == 0){
+ if(ishuge)
+ tk->req.width = 0;
+ else
+ tk->req.width = Dx(tkp->r);
+ }
+ if(ishuge)
+ tkp->view.x = 0;
+
+ ishuge = (Dy(tkp->r) >= 10000000);
+ if((tk->flag & Tksetheight) == 0){
+ if(ishuge)
+ tk->req.height = 0;
+ else
+ tk->req.height = Dy(tkp->r);
+ }
+ if(ishuge)
+ tkp->view.y = 0;
+
+ tkp->hasalpha = tkchanhastype(image->chan, CAlpha);
+ tkgeomchg(tk, &g, tk->borderwidth);
+ tksettransparent(tk, tkp->hasalpha || tkhasalpha(tk->env, TkCbackgnd));
+ tk->dirty = tkrect(tk, 0);
+}
+
+static void
+tkfreepanel(Tk *tk)
+{
+ TkPanel *tkp = TKobj(TkPanel, tk);
+ tkdelpanelimage(tk->env->top, tkp->image);
+ tkdelpanelimage(tk->env->top, tkp->matte);
+}
+
+static Point
+tkpanelview(Tk *tk)
+{
+ int dx, dy;
+ Point view;
+ TkPanel *tkp = TKobj(TkPanel, tk);
+
+ dx = tk->act.width - Dx(tkp->r);
+ dy = tk->act.height - Dy(tkp->r);
+
+ view = tkp->view;
+
+ if (dx > 0) {
+ if((tkp->anchor & (Tkeast|Tkwest)) == 0)
+ view.x -= dx/2;
+ else
+ if(tkp->anchor & Tkeast)
+ view.x -= dx;
+ }
+ if (dy > 0) {
+ if((tkp->anchor & (Tknorth|Tksouth)) == 0)
+ view.y -= dy/2;
+ else
+ if(tkp->anchor & Tksouth)
+ view.y -= dy;
+ }
+ return view;
+}
+
+static char*
+tkdrawpanel(Tk *tk, Point orig)
+{
+ Rectangle r, pr;
+ TkPanel *tkp = TKobj(TkPanel, tk);
+ Image *i;
+ int any;
+ Point view, p;
+
+ i = tkimageof(tk);
+ if (i == nil)
+ return nil;
+
+ p.x = orig.x + tk->act.x + tk->borderwidth;
+ p.y = orig.y + tk->act.y + tk->borderwidth;
+
+ view = tkpanelview(tk);
+
+ /*
+ * if the image doesn't fully cover the dirty rectangle, then
+ * paint some background in there
+ */
+ r = rectsubpt(tkp->r, view); /* convert to widget coords */
+ pr = tkrect(tk, 0);
+ any = rectclip(&r, pr); /* clip to inside widget borders */
+
+ if (!any || tkp->hasalpha || !rectinrect(tk->dirty, r))
+ draw(i, rectaddpt(tk->dirty, p), tkgc(tk->env, TkCbackgnd), nil, ZP);
+
+ if (any && rectclip(&r, tk->dirty))
+ draw(i, rectaddpt(r, p), tkp->image, tkp->matte, addpt(r.min, view));
+
+ if (!rectinrect(tk->dirty, pr)) {
+ p.x -= tk->borderwidth;
+ p.y -= tk->borderwidth;
+ tkdrawrelief(i, tk, p, TkCbackgnd, tk->relief);
+ }
+ return nil;
+}
+
+static char*
+tkpanelcget(Tk *tk, char *arg, char **val)
+{
+ TkOptab tko[3];
+ TkPanel *tkp = TKobj(TkPanel, tk);
+
+ tko[0].ptr = tk;
+ tko[0].optab = tkgeneric;
+ tko[1].ptr = tkp;
+ tko[1].optab = tkpanelopts;
+ tko[2].ptr = nil;
+
+ return tkgencget(tko, arg, val, tk->env->top);
+}
+
+static char*
+tkpanelcvt(Tk *tk, char *arg, int rel, int *p)
+{
+ char buf[Tkmaxitem];
+
+ tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
+ if(buf[0] == '\0')
+ return TkBadvl;
+ *p = atoi(buf) + rel;
+ return nil;
+}
+
+/*
+ * screen to image
+ */
+static char*
+tkpanelpanelx(Tk *tk, char *arg, char **val)
+{
+ Point p;
+ char *e;
+
+ USED(val);
+ p = subpt(tkposn(tk), tkpanelview(tk));
+ e = tkpanelcvt(tk, arg, -p.x, &p.x);
+ if (e != nil)
+ return e;
+ return tkvalue(val, "%d", p.x);
+}
+
+static char*
+tkpanelpanely(Tk *tk, char *arg, char **val)
+{
+ Point p;
+ char *e;
+
+ USED(val);
+ p = subpt(tkposn(tk), tkpanelview(tk));
+ e = tkpanelcvt(tk, arg, -p.y, &p.y);
+ if (e != nil)
+ return e;
+ return tkvalue(val, "%d", p.y);
+}
+
+/*
+ * image to screen
+ */
+static char*
+tkpanelscreenx(Tk *tk, char *arg, char **val)
+{
+ Point p;
+ char *e;
+
+ USED(val);
+ p = subpt(tkposn(tk), tkpanelview(tk));
+ e = tkpanelcvt(tk, arg, p.x, &p.x);
+ if (e != nil)
+ return e;
+ return tkvalue(val, "%d", p.x);
+}
+
+static char*
+tkpanelscreeny(Tk *tk, char *arg, char **val)
+{
+ Point p;
+ char *e;
+
+ USED(val);
+ p = subpt(tkposn(tk), tkpanelview(tk));
+ e = tkpanelcvt(tk, arg, p.y, &p.y);
+ if (e != nil)
+ return e;
+ return tkvalue(val, "%d", p.y);
+}
+
+static char*
+tkpanelconf(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ TkGeom g;
+ int bd;
+ TkOptab tko[3];
+ TkPanel *tkp = TKobj(TkPanel, tk);
+
+ tko[0].ptr = tk;
+ tko[0].optab = tkgeneric;
+ tko[1].ptr = tkp;
+ tko[1].optab = tkpanelopts;
+ tko[2].ptr = nil;
+
+ if(*arg == '\0')
+ return tkconflist(tko, val);
+
+ g = tk->req;
+ bd = tk->borderwidth;
+ e = tkparse(tk->env->top, arg, tko, nil);
+ tkgeomchg(tk, &g, bd);
+ tksettransparent(tk, tkp->hasalpha || tkhasalpha(tk->env, TkCbackgnd));
+
+ tk->dirty = tkrect(tk, 1);
+
+ return e;
+}
+
+static char*
+tkpaneldirty(Tk *tk, char *arg, char **val)
+{
+ char buf[Tkmaxitem];
+ int n, coords[4];
+ Rectangle r;
+ char *e, *p;
+ TkPanel *tkp = TKobj(TkPanel, tk);
+
+ USED(val);
+ n = 0;
+ while (n < 4) {
+ arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
+ if (buf[0] == 0)
+ break;
+ p = buf;
+ e = tkfrac(&p, &coords[n++], nil);
+ if (e != nil)
+ return TkBadvl;
+ }
+ if (n == 0)
+ r = tkp->r;
+ else {
+ if (n != 4)
+ return TkBadvl;
+ r.min.x = TKF2I(coords[0]);
+ r.min.y = TKF2I(coords[1]);
+ r.max.x = TKF2I(coords[2]);
+ r.max.y = TKF2I(coords[3]);
+ }
+ if (rectclip(&r, tkp->r)) {
+ r = rectsubpt(r, tkpanelview(tk)); /* convert to widget coords */
+ if (rectclip(&r, tkrect(tk, 0))) /* clip to visible area */
+ combinerect(&tk->dirty, r);
+ }
+ return nil;
+}
+
+static char*
+tkpanelorigin(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ Point view;
+ TkPanel *tkp = TKobj(TkPanel, tk);
+
+ e = tkxyparse(tk, &arg, &view);
+ if (e != nil) {
+ if (e == TkOparg)
+ return tkvalue(val, "%d %d", tkp->view.x, tkp->view.y);
+ return e;
+ }
+ tkp->view = view;
+ tk->dirty = tkrect(tk, 0);
+ return nil;
+}
+
+static
+TkCmdtab tkpanelcmd[] =
+{
+ "cget", tkpanelcget,
+ "configure", tkpanelconf,
+ "dirty", tkpaneldirty,
+ "origin", tkpanelorigin,
+ "panelx", tkpanelpanelx,
+ "panely", tkpanelpanely,
+ "screenx", tkpanelscreenx,
+ "screeny", tkpanelscreeny,
+ nil
+};
+
+TkMethod panelmethod = {
+ "panel",
+ tkpanelcmd,
+ tkfreepanel,
+ tkdrawpanel
+};